Mes langages de prédilection étant plutôt le C ou l'assembleur que Visual Basic pour Application, les macros ci-dessous sont probablement perfectibles et ne constituent en aucune manière des modèles de programmation VBA.
Cette macro génère la table de variances/covariances de la feuille "VarCovar" à partir des cours
de la feuille "Données".
Au passage, elle remplit aussi la liste des valeurs de la feuille "Portefeuille", ainsi que leurs
volatilités et leurs rendements historiques.
'**************************************************************************** '* Macro CreeMatriceVarCovar * '**************************************************************************** '* * '* DESCRIPTION : cree une matrice de variances/covariances a partir des * '* donnees de la feuille 'Données'. Les valeurs dont le * '* nombre de periodes est inférieur a 36 sont affichees sur * '* fond rouge pour signifier leur manque de maturite. * '* * '* ENTREE..... : Indirectement les rendements de toutes les periodes de * '* de toutes les valeurs de la feuille 'Données'. * '* * '* SORTIE..... : Feuille 'VarCovar' remplie. * '* * '* RETOUR..... : Neant. * '* * '**************************************************************************** Sub CreeMatriceVarCovar() '----- Constantes ----- Const LIGNE_TITRE_DONNEES = 1 Const PREMIERE_COLONNE_SRC = 3 Const PREMIERE_LIGNE_SRC = 4 Const PREMIERE_COLONNE_DEST = 2 Const PREMIERE_LIGNE_DEST = 5 Const NB_DATAS_MINI = 36 Const COLONNE_NOM_VALEURS_PF = 2 Const PREMIERE_LIGNE_DEST_PF = 2 Const COLONNE_VOLATILITE_VALEURS_PF = 5 Const COLONNE_RENDEMENT_VALEURS_PF = 4 '----- Variables ----- iLigSrc = PREMIERE_LIGNE_SRC iColSrc = PREMIERE_COLONNE_SRC iLigDest = PREMIERE_LIGNE_DEST iColDest = PREMIERE_COLONNE_DEST iNbSeries = 0 ' Nombre de series de donnees Dim iTabLigDeb(200, 2) As Integer ' ----- On cherche le nombre de series ----- While (Not IsEmpty(Worksheets("Données").Cells(LIGNE_TITRE_DONNEES, iColSrc - 1))) iNbSeries = iNbSeries + 1 iColSrc = iColSrc + 2 Wend iColSrc = PREMIERE_COLONNE_SRC ' ----- Pour chaque serie de donnees, on va stocker le debut et la fin ----- For i = PREMIERE_COLONNE_SRC To PREMIERE_COLONNE_SRC + (iNbSeries - 1) * 2 Step 2 iLigSrc = PREMIERE_LIGNE_SRC While (IsEmpty(Worksheets("Données").Cells(iLigSrc, i))) iLigSrc = iLigSrc + 1 Wend iTabLigDeb((i - PREMIERE_COLONNE_SRC) / 2, 0) = iLigSrc While (Not IsEmpty(Worksheets("Données").Cells(iLigSrc, i))) iLigSrc = iLigSrc + 1 Wend iTabLigDeb((i - PREMIERE_COLONNE_SRC) / 2, 1) = iLigSrc - 1 Next ' ----- On va generer la matrice de variance/covariance ----- iLigSrc = PREMIERE_LIGNE_SRC iColSrc = PREMIERE_COLONNE_SRC For i = 0 To iNbSeries - 1 iColDest = PREMIERE_COLONNE_DEST + i ' Nom valeur Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST, iColDest).Formula = "=Données!" + _ Worksheets("Données").Cells(iLigSrc - 3, i * 2 + PREMIERE_COLONNE_SRC - 1).Address Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST + iColDest - PREMIERE_COLONNE_DEST + 1, _ PREMIERE_COLONNE_DEST - 1).Formula = "=Données!" + _ Worksheets("Données").Cells(iLigSrc - 3, i * 2 + PREMIERE_COLONNE_SRC - 1).Address If (iTabLigDeb(i, 1) - iTabLigDeb(i, 0) < NB_DATAS_MINI) Then ' On met le fond en rouge Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST, iColDest).Interior.Color = RGB(255, 0, 0) Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST + iColDest - PREMIERE_COLONNE_DEST + 1, PREMIERE_COLONNE_DEST - 1).Interior.Color = RGB(255, 0, 0) End If ' Variance Worksheets("VarCovar").Cells(iLigDest + 1, iColDest).Formula = "=VARP(Données!" + _ Worksheets("Données").Cells(iTabLigDeb(i, 0), i * 2 + PREMIERE_COLONNE_SRC).Address + ":" + _ Worksheets("Données").Cells(iTabLigDeb(i, 1), i * 2 + PREMIERE_COLONNE_SRC).Address + ")" Worksheets("VarCovar").Cells(iLigDest + 1, iColDest).Interior.Color = RGB(255, 255, 128) If (iTabLigDeb(i, 0) > iTabLigDeb(j, 0)) Then iLigneDeb = iTabLigDeb(i, 0) Else iLigneDeb = iTabLigDeb(j, 0) If (iTabLigDeb(i, 1) > iTabLigDeb(j, 1)) Then iLigneFin = iTabLigDeb(i, 1) Else iLigneFin = iTabLigDeb(j, 1) Set plage1 = Range(Worksheets("Données").Cells(iLigneDeb, i * 2 + PREMIERE_COLONNE_SRC), Worksheets("Données").Cells(iLigneFin, i * 2 + PREMIERE_COLONNE_SRC)) 'Covariances For j = i + 1 To iNbSeries - 1 iColDest = iColDest + 1 Set plage2 = Range(Worksheets("Données").Cells(iLigneDeb, j * 2 + PREMIERE_COLONNE_SRC), Worksheets("Données").Cells(iLigneFin, j * 2 + PREMIERE_COLONNE_SRC)) If (iLigneFin - iLigneDeb < NB_DATAS_MINI) Then lColorTexte = RGB(128, 128, 128) Else lColorTexte = RGB(0, 0, 0) Worksheets("VarCovar").Cells(iLigDest + 1, iColDest).Value = Application.WorksheetFunction.Covar(plage1, plage2) Worksheets("VarCovar").Cells(iLigDest + 1, iColDest).Interior.Color = RGB(255, 255, 255) Worksheets("VarCovar").Cells(iLigDest + 1, iColDest).Font.Color = lColorTexte '-----Seconde partie de la matrice----- Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST + iColDest - PREMIERE_COLONNE_DEST + 1, _ PREMIERE_COLONNE_DEST + iLigDest - PREMIERE_LIGNE_DEST).Formula = "=" + Worksheets("VarCovar").Cells(iLigDest + 1, iColDest).Address Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST + iColDest - PREMIERE_COLONNE_DEST + 1, _ PREMIERE_COLONNE_DEST + iLigDest - PREMIERE_LIGNE_DEST).Interior.Color = RGB(200, 200, 200) Worksheets("VarCovar").Cells(PREMIERE_LIGNE_DEST + iColDest - PREMIERE_COLONNE_DEST + 1, _ PREMIERE_COLONNE_DEST + iLigDest - PREMIERE_LIGNE_DEST).Font.Color = lColorTexte Next j iLigDest = iLigDest + 1 '----------------------------------------------------------------------------- ' On va recopier les noms des valeurs dans la seconde colonne du portefeuille '----------------------------------------------------------------------------- ' ----- Nom valeur ----- Worksheets("Portefeuille").Cells(PREMIERE_LIGNE_DEST_PF + i, COLONNE_NOM_VALEURS_PF).Formula = "=Données!" + _ Worksheets("Données").Cells(iLigSrc - 3, i * 2 + PREMIERE_COLONNE_SRC - 1).Address If (iTabLigDeb(i, 1) - iTabLigDeb(i, 0) < NB_DATAS_MINI) Then ' On met le fond en rouge Worksheets("Portefeuille").Cells(PREMIERE_LIGNE_DEST_PF + i, COLONNE_NOM_VALEURS_PF).Interior.Color = RGB(255, 0, 0) Else Worksheets("Portefeuille").Cells(PREMIERE_LIGNE_DEST_PF + i, COLONNE_NOM_VALEURS_PF).Interior.ColorIndex = xlColorIndexNone End If ' ----- Rendements historiques ----- Worksheets("Portefeuille").Cells(PREMIERE_LIGNE_DEST_PF + i, COLONNE_RENDEMENT_VALEURS_PF).Formula = "=(Données!" + Worksheets("Données").Cells(iLigneFin + 2, i * 2 + PREMIERE_COLONNE_SRC).Address + ")^12-1" ' ----- Volatilité mensuelle ----- Worksheets("Portefeuille").Cells(PREMIERE_LIGNE_DEST_PF + i, COLONNE_VOLATILITE_VALEURS_PF).Formula = "=StDevP(Données!" + _ Worksheets("Données").Cells(iLigneDeb, i * 2 + PREMIERE_COLONNE_SRC).Address + ":" + _ Worksheets("Données").Cells(iLigneFin, i * 2 + PREMIERE_COLONNE_SRC).Address + ")" Next i End Sub
Le module "Optimisation" contient plusieurs macros :
'**************************************************************************** '* Module Optimisation * '**************************************************************************** '* * '* DESCRIPTION........ : Module comprenant les fonctions necessaires à * '* l'optimisation d'un portefeuille, ainsi qu'au * '* tracé de la frontière efficiente. * '* * '* AUTEUR............. : "La Bourse pour les nains" * '* http://wwww.bnains.org/ * '* * '* DATE DE CREATION... : 2000 * '* * '* FONCTIONS EXPORTEES : * '* Neant. * '* * '* MACROS EXPORTEES... : * '* OptimiseVolPF() * '* Cherche la composition de portefeuille permettant d'obtenir la * '* volatilité la plus faible pour un rendement donné. * '* OptimiseRendementPF() * '* Cherche la composition de portefeuille permettant d'obtenir le * '* rendement le plus élevé pour une volatilité donnée. * '* TracerFrontiereEfficiente() * '* Pour une liste de rendements donnée, cherche les volatilités les * '* plus faibles de manière à produire une liste de couples (rendement, * '* volatilité) permettant de tracer la frontière efficiente d'une liste * '* de valeurs. * '* * '**************************************************************************** '* MODIFIE LE ../../.... PAR ...................... * '* DESCRIPTION DE LA MODIFICATION : * '* * '**************************************************************************** Const iNbTitres = 48 Const szCelluleMaximumParLigne = "$56" Const szCelluleMinimumParLigne = "$55" Const szCelluleObjectifRendement = "$52" Const szCelluleRendementCalcule = "C51" Const szCelluleSommePoidsValeurs = "C50" Const szCelluleVariance = "C52" Const szCelluleObjectifEcartType = "E53" Const szCelluleEcartTypeAnnuelCalcule = "C54" '**************************************************************************** '* Macro OptimiseVolPF * '**************************************************************************** '* * '* DESCRIPTION : cherche les proportions de valeur permettant d'obtenir la * '* variance la plus petite pour le rendement donné. * '* * '* ENTREE..... : indirectement, liste des valeurs, minimum et maximum de * '* proportion dans le portefeuille et rendement souhaite. * '* * '* SORTIE..... : indirectement, la variance calculée. * '* * '* RETOUR..... : Neant. * '* * '**************************************************************************** Public Sub OptimiseVolPF() '************************************************************************ '* On programme le solveur pour minimiser la variance en jouant sur les * '* proportions des valeurs * '************************************************************************ ' ----- Préparation de l'environnement de travail ----- ' On se positionne dans la bonne feuille Worksheets("Portefeuille").Activate ' Et dans la bonne cellule (pour éviter un bug du solver avec certaines versions d'Excel) Range(szCelluleVariance).Select ' Reset du solveur SolverReset ' On met toutes les proportions de valeur à 0 For iValeur = 2 To iNbTitres + 1 Cells(iValeur, 3).Value = 0 Next iValeur ' Objectif : Minimiser la variance en faisant varier les proportions des valeurs SolverOk szCelluleVariance, maxMinVal:=2, byChange:=Range("C2:C49") ' ----- On cree les contraintes ----- 'Somme des poids des valeurs = 1 (soit 100%) SolverAdd cellRef:=szCelluleSommePoidsValeurs, relation:=2, formulaText:=1 ' Valeurs interdites (toutes les valeurs non cochées) For iValeur = 1 To iNbTitres If Cells(iValeur, 1).Value = "N" Then SolverAdd Cells(iValeur, 3), relation:=2, formulaText:=0 Next iValeur 'Poids de chaque valeur <= maximum par ligne SolverAdd cellRef:="C2:C49", relation:=1, formulaText:=szCelluleMaximumParLigne 'Poids de chaque valeur >= minimum par ligne SolverAdd cellRef:="C2:C49", relation:=3, formulaText:=szCelluleMinimumParLigne 'Rendement attendu = celui demandé SolverAdd cellRef:=szCelluleRendementCalcule, relation:=2, formulaText:=szCelluleObjectifRendement ' ----- On indique maintenant au solveur qu'il doit bosser... ----- SolverSolve (True) SolverFinish End Sub '**************************************************************************** '* Macro OptimiseRendementPF * '**************************************************************************** '* * '* DESCRIPTION : cherche les proportions de valeur permettant d'obtenir le * '* rendement le plus élevé pour la variance donnée. * '* * '* ENTREE..... : indirectement, liste des valeurs, minimum et maximum de * '* proportion dans le portefeuille et variance souhaitée. * '* * '* SORTIE..... : indirectement, la rendement calculé. * '* * '* RETOUR..... : Neant. * '* * '**************************************************************************** Public Sub OptimiseRendementPF() ' ----- Préparation de l'environnement de travail ----- ' On se positionne dans la bonne feuille Worksheets("Portefeuille").Activate ' Et dans la bonne cellule (pour éviter un bug du solver avec certaines versions d'Excel) Range(szCelluleVariance).Select ' Reset du solveur SolverReset ' On met toutes les proportions de valeur à 0 For iValeur = 1 To iNbTitres Cells(iValeur, 3).Value = 0 Next iValeur ' Objectif : Maximiser le rendement en faisant varier les proportions des valeurs SolverOk szCelluleRendementCalcule, maxMinVal:=1, byChange:=Range("C2:C49") ' ----- On cree les contraintes ----- 'Somme des poids des valeurs = 1 (soit 100%) SolverAdd cellRef:=szCelluleSommePoidsValeurs, relation:=2, formulaText:=1 ' Valeurs interdites (toutes les valeurs non cochées) For iValeur = 2 To iNbTitres + 1 If Cells(iValeur, 1).Value = "N" Then SolvAdd Cells(iValeur, 3), relation:=2, formulaText:=0 Next iValeur 'Poids de chaque valeur <= maximum par ligne SolverAdd cellRef:="C2:C49", relation:=1, formulaText:=szCelluleMaximumParLigne 'Poids de chaque valeur >= minimum par ligne SolverAdd cellRef:="C2:C49", relation:=3, formulaText:=szCelluleMinimumParLigne 'Volatilité attendue = celle demandée SolverAdd cellRef:=szCelluleEcartTypeAnnuelCalcule, relation:=2, formulaText:=szCelluleObjectifEcartType ' ----- On indique maintenant au solveur qu'il doit bosser... ----- SolverSolve (True) SolverFinish End Sub '**************************************************************************** '* Macro TracerFrontiereEfficiente * '**************************************************************************** '* * '* DESCRIPTION : cherche la variance la plus petite pour une liste de * '* rendements donnés. * '* * '* ENTREE..... : indirectement, liste des valeurs, minimum et maximum de * '* proportion dans le portefeuille et liste des rendements * '* souhaités. * '* * '* SORTIE..... : indirectement, les couples (rendement,variance) calculés. * '* * '* RETOUR..... : Neant. * '* * '**************************************************************************** Public Sub TracerFrontiereEfficiente() Const COLONNE_RENDEMENTS_SOUHAITES = 13 '**************************************************************** '* Maintenant, on va itérer pour tracer la frontière efficiente * '**************************************************************** bMax = False i = 2 j = COLONNE_RENDEMENTS_SOUHAITES Do While (Not (IsEmpty(Cells(i, j))) And bMax = False) ' On fixe le rendement souhaite Cells(52, 5).Value = Cells(i, j) ' On optimise la variance en consequence Call OptimiseVolPF ' On recupere le rendement Cells(i, j + 1).Value = Cells(51, 3) ' On recupere la variance annuelle Cells(i, j + 2).Value = Cells(54, 3) i = i + 1 ' Si on n'est pas parvenu a ameliorer, alors on arrete If i > 3 And Cells(i - 2, j + 1).Value = Cells(i - 1, j + 1).Value Then bMax = True Loop End Sub
Remarques :
La constante "iNbTitres" pourait avantageusement être remplacée par une petite fonction comptant
les titres.
Voilà, c'est tout. Tout ceci peut bien sûr être amélioré. A vous de jouer...
Début de la page Sommaire du risque Sommaire de l'optimisation Sommaire du site
Rubriques
Meilleurs courtiers en Bourse
Meilleurs PEA
Toutes les données du CAC40
Livres finance et Bourse
Newsletter
Pour recevoir nos derniers articles, détachements de dividendes et offres de placements :
Nous contacter ou nous suivre sur les réseaux
Site hébergé par OVH - 2 rue Kellermann - 59100 Roubaix - France - Tel : 09 72 10 10 10