Numerosas son las líneas de trabajo futuro que admite el problema que aquí hemos tratado. A continuación se sugieren algunas propuestas, siguiendo
CAPÍTULO 9. CONCLUSIONES Y TRABAJO FUTURO la distribución por partes que componen el trabajo:
Parte I:
• Estudio de la literatura: la gestión del cŕedito comercial es un tema poco tratado en la literatura científica, por tanto los eventuales nuevos estudios adquieren una mayor relevancia.
• Estudio de cómo elegir el periodo que contenga los clientes del portafolio: en el trabajo se han tomado, como componentes del portafolio, los clientes que vendrán a la PYME en un determi- nado periodo. La elección de ese periodo podría estar basada en los periodos de crédito comercial concedidos habitualmente a los clientes. También hay que tomar en cuenta en qué medida empeo- ran nuestras predicciones de crédito comercial frente al tiempo. Si el periodo es tomado demasiado largo las funciones de probabili- dad que determinan la exposición en impago de nuestros clientes podrían provocar que el modelo no funcione correctamente. • Estudio de la creación de un portafolio dinámico en dos vertientes:
en lo referente a las condiciones de TC de cada cliente del portafo- lio y a los clientes que configura dicho portafolio. De este modo se usaría un único portafolio en lugar un portafolio para cada peri- odo. Esto es así ya que los clientes del portafolio se modificarían según se van cumpliendo los periodos de TC concedidos. Por una parte se eliminarían del portafolio los clientes que han cumplido sus obligaciones, por otra parte se añadirían futuros clientes según nuevas predicciones que se suceden en el tiempo.
Parte II: • Modelo:
◦ Existen discrepancias en la literatura de la gestión de crédi- to sobre si utilizar el RAROC como paŕametro a optimizar. Se propone estudiar la optimización de otros parámetros e incluso un planteamiento multiobjetivo del problema.
CAPÍTULO 9. CONCLUSIONES Y TRABAJO FUTURO ◦ Integrar el periodo del TC en el modelo: en el trabajo se ha considerado que mantiene una relación discreta y lineal con la cantidad de crédito comercial. No obstante la inserción del periodo de TC supone un gran reto y no está clara si su aportación será relevante. La integración de periodos de de- scuento también aumentaría el realismo del modelo pero la complejidad aumentaría exponencialmente.
◦ Extensión del modelo: integrar otros parámetros estocásticos como el número de clientes que harán pedidos o el beneficio neto por cada pedido.
◦ Elaboración de modelos alternativos: el cálculo de las pérdidas inesperadas es complejo y se podría realizar mediante otras técnicas como la simulación.
◦ Eliminar simplificaciones del modelo: integrar la restricción de cantidad máxima de TC que se puede ofrecer a un cliente y cantidad máxima de TC que se puede conceder en todo el portafolio. Esto a su vez exigiría una modificación del algorit- mo de resolución.
◦ En el modelo se ha empleado el paradigma Default Mode (el prestatario incurre en impago o no), también existe la posi- bilidad de usar el paradigma Mark-to-Market donde tambińe se considera que el crédito del prestatario se puede deteriorar. • Algoritmo:
◦ Análisis del orden de complejidad del algoritmo: para deter- minar la eficiencia y recursos necesarios del algoritmo.
◦ Modificación del algoritmo simheurístico: la combinación de simulación y metaheurística se podría realizar de forma dis- tinta.
◦ Uso de otras metaheurísticas: se han usado en este trabajo los algoritmos genéticos, que suelen ser buenos para resolver todo tipo de problemas. Se podría estudiar el uso de otras metaheurísticas que estuvieran más adaptadas a este tipo de
CAPÍTULO 9. CONCLUSIONES Y TRABAJO FUTURO problemas.
Parte III:
• Optimización del código: el algoritmo simheurístico desarrollado es altamente paralelizable. Tanto la parte de simulación como la metaheurística son idóneos para emplear computación paralela. Con esto se conseguiría reducir significativamente el coste com- putacional del DSS.
• Creación de una GUI: con el lenguaje de programación empleado se puede elaborar una GUI o interfaz de usuario que incremente la facilidad de uso del programa.
Parte IV:
• Implementar pruebas para determinar qué valores deben tomar los parámetros de entrada del test para resolver eficazmente el proble- ma: tanto los referentes al algoritmo genético (tasa de mutaciones, tasa de cruces, tamaño de la población, número en la élite) como los referentes a la simulación (número de simulaciones).
• Identificar, para el algoritmo simple de límite de probabilidad de impago, cual es el límite que más rendimiento ofrece.
• Comparar los resultados del test con otros algoritmos simples: que combinen indicadores como la probabilidad de impago y la exposición al impago, y que permitan el ofrecimiento de porciones del crédito comercial demandado.
Bibliografía
[1] S Christian Albright, Christopher James Zappe, and Wayne L Winston. Data Analysis, Optimization, and Simulation Modeling. South-Western, 2011.
[2] J. Baggott. Origins: The Scientific Story of Creation. OUP Oxford, 2015.
[3] Peter Checkland. Systems thinking, systems practice. 1981.
[4] Ian Iscoe, Alexander Kreinin, Helmut Mausser, and Oleksandr Ro- manko. Portfolio credit-risk optimization. Journal of Banking & Fi- nance, 36(6):1604–1615, 2012.
[5] Lee Jacobson and Burak Kanber. Genetic Algorithms in Java Basics. Springer, 2015.
[6] Angel A. Juan, Javier Faulin, Scott E. Grasman, Markus Rabe, and Gonçalo Figueira. A review of simheuristics: Extending metaheuristics to deal with stochastic combinatorial optimization problems. Operations Research Perspectives, 2:62 – 72, 2015.
[7] Sean Luke. 1 essentials of metaheuristicsl. 1.
[8] M. Lutz. Programming Python. O’Reilly Media, 2006.
[9] Harry Markowitz. Portfolio selection. The Journal of Finance, 7(1):77– 91, 1952.
BIBLIOGRAFÍA [10] Anna Nagurney. Network economics. Handbook of Computational
Econometrics, pages 429–486, 2009.
[11] F. Nelli. Python Data Analytics: Data Analysis and Science using pan- das, matplotlib and the Python Programming Language. Apress, 2015. [12] M. Pidd. Tools for Thinking: Modelling in Management Science. Wiley,
2009.
[13] Michael Pidd. Just modeling through: A rough guide to modeling. In- terfaces, 29(2):118–132, 1999.
[14] C.T. Reviews. e-Study Guide for: Fundamentals of Corporate Finance and Standard and Poors Educational Version of Market Insight by Breasley, ISBN 9780077263348. Cram101, 2012.
[15] Stewart Robinson. Simulation: the practice of model development and use. Palgrave Macmillan, 2014.
[16] C. Smithson. Credit Portfolio Management. Wiley Finance. Wiley, 2003. [17] John D Sterman. Business dynamics: systems thinking and modeling for
a complex world, volume 19. Irwin/McGraw-Hill Boston, 2000.
[18] El-Ghazali Talbi. Metaheuristics: from design to implementation, vol- ume 74. John Wiley & Sons, 2009.
[19] Manuel Terrádez Gurrea. Análisis del riesgo de crédito comercial en pymes mediante técnicas de minería de datos. 2014.
[20] Gregory Turnbull-Schwartz. The limits to diversification. Credit, 2010. [21] Víctor García Vaquero and Francisco Alonso. El crédito comercial en
españa: importancia relativa y evolución reciente. Boletín económico- Banco de España, (2):67–77, 2011.
[22] Rosemary H Wild, Kenneth A Griggs, and Tanya Downing. A frame- work for e-learning as a tool for knowledge management. Industrial Management & Data Systems, 102(7):371–380, 2002.
BIBLIOGRAFÍA [23] Benjamin S Wilner. The exploitation of relationships in financial dis- tress: The case of trade credit. The Journal of Finance, 55(1):153–178, 2000.
Apéndices
A.
Código del DSS (DSS.py)
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*- 3
4 5 """
6 - Title: Trade Credit Portfolio Optimization Problem 7
8 - Summary: The Trade Credit Portfolio Optimization Problem
(TCPOP) consists in
,→
9 finding efficient solutions for optimize the
risk-return profile
,→
10 of a trade credit portfolio. This solutions can be
defined as a
,→
11 vector of tuples where each tuple consists of the
trade credit
,→
12 amount and the trade credit period for any given
customer.
,→
13 In this script is implemented a framework to solve
the TCPOP using
,→
14 a method based on simheuristics. The framework can
be summarised
,→
A. CÓDIGO DEL DSS
16
17 1. Data acquisition and processing.
18 2. Create a population of possible solutions. 19 3. Calculate the portfolio performance metrics. 20 4. Solve the TCPOP usign simheuristics.
21 5. Create a report. 22
23 The algorithm proposed herein combines Monte-Carlo
Simulaton with
,→
24 Genetic Algorithms. While the Monte-Carlo
Simulation deals with the
,→
25 stochasticity of the problem, the Genetic
Algorithm metaheuristic
,→
26 deals with the non-linearity, non-convexity nature
of the problem.
,→
27 The Genetic Algorithm is a method for exploring the
space of
,→
28 solutions efficiently without getting stuck in a
local optimum.
,→
29
30 - Example: python DSS.py -d TCPOP_data.csv -t 2 31 python DSS.py -d TCPOP_data.csv 32 python DSS.py -s STCPOP_data.csv 33 python DSS.py --ds STCPOP_data.csv
34 python DSS.py --test /home/user/Documents/Test 35 python DSS.py --test Test
36
37 - Author: Juan Felici <[email protected]> 38
39 - Date: May 17, 2017 40
41 - Tested on Fedora 25 42 """
A. CÓDIGO DEL DSS
43 44
45 #%% Import modules, packages and libraries. 46 """
47 One of the features of Python language is the huge set of
modules, packages and
,→
48 libraries we can import. These are well formated Python code
we can reuse in
,→
49 our proyect to extend its capabilities. We can differentiate
between system
,→
50 imports (wich are contained in the Python Standard Library)
and third party
,→
51 imports (that we must install externally). More information
about Python
,→
52 modules cand be find in
<https://docs.python.org/2/install/index.html>
,→
53
54 In this proyect are used the following packages: 55 1. System imports:
56 - sys: This module provides access to some variables
used or maintained
,→
57 by the interpreter and to functions that
interact strongly with
,→
58 the interpreter. 59 (link:
https://docs.python.org/2/library/sys.html)
,→
60 - time: This module provides various time-related
functions.
,→
61 (link:
https://docs.python.org/2/library/time.html)
,→
62 - optparse: Enable command-line option parsing. 63 (link:
https://docs.python.org/2/library/optparse.html)
A. CÓDIGO DEL DSS
64 - os: This module provides a portable way of using
operating system
,→
65 dependent functionaly.
66 (link: https://docs.python.org/2/library/os.html) 67 2. Third-pary imports:
68 - pandas: Python Data Analisys Library. 69 (link: http://pandas.pydata.org/)
70 - numpy: Fundamental package for scientific computing
with Python. ,→ 71 (link: http://www.numpy.org/) 72 """ 73 74 # 1-System imports:
75 import time, os, optparse, sys 76 # 2-Third-party imports:
77 import pandas as pd, numpy as np 78
79
80 #%% Load data. 81 """
82 The first step in the framework is to load the data. The
pandas package is used
,→
83 to create a data frame, then a set of attributes are created
and stored in a
,→
84 Data object.
85 The data is load from a CSV file where are stored the client
specific ,→ 86 parameters: 87 - Clients. 88 - Exposure At Default. 89 - Probability of Default. 90 - Default Volatility 91 - Sector_{n}
A. CÓDIGO DEL DSS
92 - Loss Given Default. 93 - Net Income.
94 """ 95
96 class Data(object): 97 """
98 Data class allows to extrat sensitive data of the file. 99 """
100
101 def __init__(self, data_file): 102 """
103 Initialize Data module. 104
105 Parameters 106 ---
107 data_file : str (CSV file)
108 Contains the required information of the
clients. ,→ 109 110 Returns 111 --- 112 Data object 113 114 Notes 115 --- 116 Abbreviations used:
117 - Exposure At Default: EAD 118 - Probability of Default: PD 119 - Default Volatility: DV 120 - Loss Given Default: LGD 121 - Net Income: NI 122 - Default Event: DE 123
A. CÓDIGO DEL DSS 124 Examples 125 --- 126 >>> data = Data('clients.csv') 127 128 >>> data = Data('~/Documetns/clients.csv') 129 """ 130
131 # Create a data frame using pandas package:
132 mydata = pd.read_csv(data_file) 133
134 # Data class attributes:
135 """
136 Client specific parameters: 137 """
138 self.EAD = mydata['Exposure At Default'] 139 self.PD = mydata['Probability of Default'] 140 self.DV = mydata['Default Volatility'] 141 self.LGD = mydata['Loss Given Default'] 142 self.NI = mydata['Net Income']
143 try:
144 self.DE = mydata['Default Event'] 145 except KeyError:
146 pass
147 """
148 General parameters: 149 """
150 self.clientNames = mydata['Clients'] 151 self.numberSectors =
int(str(mydata.keys()).count('Sector'))
,→
152 self.numberClients = len(self.clientNames) 153 self.matrixSectors = [self.numberSectors*[0] \
154 for i in range(self.numberClients)]
A. CÓDIGO DEL DSS
156 for i in range(self.numberClients)]
157 """
158 Sector specific parameters: 159 """
160 self.variationCoefficient = range(self.numberSectors) 161 self.meanDefaultRate = range(self.numberSectors)
162 self.defaultRateVolatility = range(self.numberSectors) 163
164 # Number of simulations:
165 """
166 Specifying the number of simulations we want to run in
the MCS.
,→
167 """
168 self.numberSimulations = 100 169
170 # Recalculating Data class attributes initialized
before:
,→
171 """
172 Recalculating self.matrixSectors: 173 """
174 for i in range(self.numberSectors):
175 for j in range(self.numberClients):
176 self.matrixSectors[j][i]=mydata['Sector_'+str(i⌋ +1)][j]
,→
177 """
178 Recalculating self.meanDefaultRate: 179 """
180 for sector in range(self.numberSectors): 181 self.meanDefaultRate[sector] =
sum([self.matrixSectors[i][sector] \
,→
182 *self.PD[i] for i in range(self.numberClients)]) 183 """
A. CÓDIGO DEL DSS
185 """
186 for sector in range(self.numberSectors): 187 self.defaultRateVolatility[sector] = \
188 sum([self.matrixSectors[i][sector]*self.DV[i] \
189 for i in range(self.numberClients)])
190 self.defaultRateVolatility[0]=0 191 """
192 Recalculating self.variationCoefficient: 193 """
194 for sector in range(self.numberSectors): 195 if self.meanDefaultRate[sector] == 0: 196 self.variationCoefficient[sector] = 0
197 else:
198 self.variationCoefficient[sector] = \
199 self.defaultRateVolatility[sector]/self.meanDef⌋ aultRate[sector]
,→
200 """
201 Recalculating self.correlations: 202 """
203 for i in range(self.numberClients):
204 for j in range(self.numberClients):
205 self.correlations[i][j]=np.sqrt(self.PD[i]*self⌋ .PD[j])*\
,→
206 sum([self.matrixSectors[i][k]*self.matrixSector⌋ s[j][k]\
,→
207 *self.variationCoefficient[k]**2 for k in \
208 range(self.numberSectors)])
209 """
210 Another way to calculate correlations using numpy
library:
,→
211 """
212 self.correlations = np.corrcoef(self.matrixSectors) 213
A. CÓDIGO DEL DSS
214 # Functions for data processing in stochastic portfolios: 215 """
216 The method extractDistributions extracts the distribution 217 information of the data file. It returns a well formated
matrix of
,→
218 distributions. 219 """
220 def extractDistributions(self): 221 for i in range(len(self.EAD)): 222 self.EAD[i] =
[self.EAD[i][0:self.EAD[i].index('(')],\
,→
223 self.EAD[i][self.EAD[i].index('(')+1:self.EAD[i].i⌋ ndex(',')],\
,→
224 self.EAD[i][self.EAD[i].index(',')+1:self.EAD[i].i⌋ ndex(')')]]
,→
225 self.EAD[i][1] = int(self.EAD[i][1]) 226 self.EAD[i][2] = int(self.EAD[i][2]) 227 disMatrix = self.EAD[:]
228 return disMatrix 229 """
230 Once we have the distribution matrix we can pass it to the
generateEAD
,→
231 method in order to create a matrix with feasible Exposure
at Default
,→
232 values. This method takes the distribution matrix and
creates a set of EADs
,→
233 for the number of simulations selected. 234 """
235 def generateEAD(self, disMatrix):
236 eadMatrix = [len(disMatrix)*[0] for i in
range(self.numberSimulations)]
,→
237 for k in range(self.numberSimulations):
A. CÓDIGO DEL DSS 239 if disMatrix[i][0] == 'Uniform': 240 eadMatrix[k][i] = np.random.uniform(disMatrix[i][1],\ ,→ 241 disMatrix[i][2])
242 elif disMatrix[i][0] == 'Normal':
243 eadMatrix[k][i] = np.random.normal(disMatrix[i][1],\ ,→ 244 disMatrix[i][2]) 245 return eadMatrix 246
247 # Functions for data processing in test mode: 248 """
249 The files generated for testing pupusoses contain three
EADs:
,→
250 - EAD deterministic. 251 - EAD stochastic. 252 - EAD real.
253 In order to manipulate this three types a specific funtion
is created.
,→
254 """
255 def extractEADs(self):
256 l = []
257 for i in range(len(self.EAD)):
258 EAD_deterministic =
int(self.EAD[i][1:self.EAD[i].index(',')])
,→
259 EAD_actual =
int(self.EAD[i][-self.EAD[i].index(','):-1])
,→
260 EAD_stochastic =
self.EAD[i][self.EAD[i].index(',')+3:-self.\
,→ 261 EAD[i].index(',')-3] 262 l.append([EAD_deterministic, EAD_stochastic, EAD_actual]) ,→ 263 return l
A. CÓDIGO DEL DSS
264
265 # Functions for data processing in dynamic portfolio: 266 """
267 A static matrix of EADs are created in order to represent
the credit
,→
268 amount already delivered. 269 """
270 def staticEAD(self, matrixEAD, EAD): 271 return [matrixEAD[k]+[EAD] for k in
range(self.numberSimulations)]
,→
272 """
273 This function determine the final matrix as the
conjunction of a static
,→
274 matrix and a dynamic matrix. 275 """
276 def determineEAD(self, static, dynamic): 277 return [static[k]+dynamic[k] for k in
range(self.numberSimulations)]
,→
278 279
280 #%% Portfolio performance metrics. 281 """
282 The performance metrics that characterize the porfolio are: 283 1. Expected Return.
284 2. Expected Loss. 285 3. Unexpected Loss. 286 4. Economic Capital.
287 5. Risk-Adjusted Return on Capital (RAROC). 288 """
289
290 class Portfolio(Data): 291 """
A. CÓDIGO DEL DSS
292 Portfolio class allows to create a Portfolio object and
calculate its
,→
293 performance metrics. The Portfolio class inherit the
attributes of the
,→
294 Data class. 295 """
296
297 # Methods for calculating portfolio performance metrics: 298 """
299 The expeted return is the amount of money we expect to
profit beforehand.
,→
300 """
301 def expectedReturn(self, individual):
302 return sum([x*e*(1+n)*(1-p) for x,e,n,p in \
303 zip(individual.getChromosome(), \
304 self.EAD, self.NI,
self.PD)])-self.expectedLoss(individual)
,→
305 """
306 The expected loss is the amount of money we expect to loss
beforehand.
,→
307 """
308 def expectedLoss(self, individual): 309 return sum([x*e*l*p for x,e,l,p in
zip(individual.getChromosome(), \
,→
310 self.EAD, self.LGD, self.PD)]) 311 """
312 The unexpected loss is related to the variation of the
expected loss.
,→
313 """
314 def unexpectedLoss(self, individual):
315 individual_UL = [x*self.EAD[i]*self.LGD[i]*self.DV[i] for x,i in \
A. CÓDIGO DEL DSS
316 zip(individual.getChromosome(),
range(self.numberClients))]
,→
317 UL_p = 0
318 for i in range(self.numberClients):
319 for j in range(self.numberClients):
320 UL_p += individual_UL[i]*individual_UL[j]\ 321 *self.correlations[i][j]
322 return np.sqrt(UL_p) 323 """
324 The economic capital is usually the amount of money needed
to ensure that
,→
325 the company stays solvent. 326 """
327 def economicCapital(self, individual):
328 return self.unexpectedLoss(individual)-self.expectedLo⌋ ss(individual)
,→
329 """
330 The Risk-Adjusted Return on Capital (RAROC) combines the
expected return
,→
331 and the economic capital, it is used as the objective
function.
,→
332 """
333 def RAROC(self, individual):
334 return self.expectedReturn(individual)/self.economicCa⌋ pital(individual)
,→
335 """
336 The clacBenefit method is used for testing purposes as a
mesure of per-
,→
337 formance. The calcBenefit method returns the net amount of
money earned
,→
338 given the default event, the actual EAD and the portfolio
weights.
,→
A. CÓDIGO DEL DSS
340 def calcBenefit(self, actualEAD, individual): 341 return sum([x*e*(n*(1-p)-p) for x,e,n,p in
zip(actualEAD, individual,\
,→
342 self.NI, self.DE)]) 343
344
345 #%% Individual. 346 """
347 An individual is a possible solution of the problem. An
individual
,→
348 is represented by its chromosome. Each gene of the chromosome
is
,→
349 the portion of TC the clients requests.
350 The chromosome is in the form [0.23, 0.43, 0, ...], where the
nth
,→
351 location of the list correspond to the client number n. 352 """
353
354 class Individual(object): 355
356 def __init__(self, chromosome): 357 """
358 Initialize Individual module. 359
360 Parameters 361 ---
362 chromosome : int, list
363 Contains the chromosome (list) or its length
(int). ,→ 364 365 Returns 366 --- 367 nothing
A. CÓDIGO DEL DSS
368
369 Notes 370 ---
371 This class is written using setters and getters for
the sake
,→
372 of redability. Since the Python language does not
support
,→
373 encapsulation in the way Java does. 374
375 Two attributes are created:
376 1. Chromosome : weights in the portfolio. 377 2. Fitness : metric of the objective function. 378 379 Examples 380 --- 381 >>> individual_1 = Individual(3) 382 383 >>> individual_2 = Individual([0.43, 0, 0.84]) 384 """ 385
386 self.chromosome = chromosome 387 self.fitness = -1
388 if type(self.chromosome) is int:
389 self.chromosome = range(self.chromosome) 390 for gene in self.chromosome:
391 self.setGene(gene , round(np.random.random(),2)) 392
393 # Setters and getters for Individual class: 394 """
395 Gene level: 396 """
397 def setGene(self, offset, gene): 398 self.chromosome[offset] = gene
A. CÓDIGO DEL DSS
399
400 def getGene(self, offset):
401 return self.chromosome[offset] 402 """
403 Chromosome level: 404 """
405 def getChromosomeLength(self): 406 return len(self.chromosome) 407
408 def getChromosome(self): 409 return self.chromosome 410 """
411 Fitness parameter: 412 """
413 def setFitness(self, fitness): 414 self.fitness = fitness 415
416 def getFitness(self): 417 return self.fitness 418
419
420 #%% Population. 421 """
422 A population is a set of individuals. In a genetic algorithm
the populations
,→
423 are replaced dynamically as they are evolving, generation to
generation.
,→
424 """ 425
426 class Population(object): 427
428 _populationFitness = -1 429
A. CÓDIGO DEL DSS
430 def __init__(self, populationSize, chromosomeLength): 431 """
432 Initialize Population module. 433
434 Parameters 435 ---
436 populationSize : int
437 Contains the number of individuals in the
population.
,→
438 chromosomeLength : int
439 Contains the chromosome length of each
individual. ,→ 440 441 Returns 442 --- 443 nothing 444 445 Notes 446 ---
447 As in the Individual class the Population class makes
use of getters
,→
448 and setters in spite of this is unnecessary. 449 450 Examples 451 --- 452 >>> population = Population(1000, 5) 453 """ 454
455 self.population = range(populationSize)
456 for ind in range(populationSize):
457 individual = Individual(chromosomeLength) 458 self.population[ind] = individual
A. CÓDIGO DEL DSS
460 # Individual level: 461 """
462 Some methods to manage the individuals in the population: 463 """
464 def getFittest(self, offset):
465 self.population.sort(key=lambda x: x.getFitness(), reverse=True)
,→
466 return self.population[offset] 467
468 def getIndividuals(self): 469 return self.population 470
471 def setIndividual(self, offset, individual): 472 self.population[offset] = individual 473
474 def getIndividual(self, offset): 475 return self.population[offset] 476
477 # Population level; 478 """
479 Methods to calculate parameters associated with the
population:
,→
480 """
481 def size(self):
482 return len(self.population) 483
484 def setPopulationFitness(self, fitness): 485 self.populationFitness = fitness 486
487 def getPopulationFitness(self): 488 return self.populationFitness 489
A. CÓDIGO DEL DSS
491 #%% Genetic algorithm. 492 """
493 The genetic algorithm is the method used in this framework to
explore the
,→
494 space of solutions. The GeneticAlgorithm class is defined in
order to manage
,→
495 the individuals relations and the dynamic change of the
populations. ,→ 496 """ 497 498 populationSize = 1000 499 mutationRate = 0.01 500 crossoverRate = 0.90 501 elitismCount = 100 502
503 class GeneticAlgorithm(object): 504
505 def __init__(self, chromosomeLength): 506 """
507 Initialize Population module. 508
509 Parameters 510 ---
511 populationSize : int
512 Contains the number of individuals in the
population.
,→
513 chromosomeLength : int
514 Contains the chromosome length of each
individual.
,→
515 mutationRate : float
516 Contains the probability of mutation. 517 crossoverRate : float
A. CÓDIGO DEL DSS
518 Frequency of chromosomal crossover between
individuals.
,→
519 elitismCount : int
520 Number of fittest individuals that survive
in a generation. ,→ 521 522 Returns 523 --- 524 nothing 525 526 Examples 527 --- 528 >>> ga = GeneticAlgorithm(1000, 5, 0.01, 0.90, 100) 529 """ 530
531 self.populationSize = populationSize 532 self.chromosomeLength = chromosomeLength 533 self.mutationRate = mutationRate
534 self.crossoverRate = crossoverRate 535 self.elitismCount = elitismCount 536
537 # Methods implemented in the GengeticAlgorithm class: 538 """
539 This method creates an initial population (zero
generation). It calls the
,→
540 Population class. 541 """
542 def initializePopulation(self):
543 population = Population(self.populationSize,
self.chromosomeLength)
,→
544 return population 545 """
A. CÓDIGO DEL DSS
546 This method calculates the fitness of each individual. The
fitness function
,→
547 or objective function is the Portfolio.RAROC() method. 548 """
549 def calculateFitness(self, individual, portfolio): 550 fitness = portfolio.RAROC(individual)
551 individual.setFitness(fitness) 552 return fitness
553 """
554 In order to evaluate a population this method calls the
calculateFitness
,→
555 method for each individual, setting each fitness. Then is
setted the
,→
556 population fitness, that is a metric to evaluate how good
the individuals
,→
557 are in each population. 558 """
559 def evaluatePopulation(self, population, portfolio): 560 populationFitness = 0
561 for individual in population.getIndividuals():
562 populationFitness +=
self.calculateFitness(individual, portfolio)
,→
563 population.setPopulationFitness(populationFitness) 564 """
565 This method creates a new population resulted from the
crossover of its
,→
566 individuals.
567 Some crossover procedures: 568 - Single point.
569 - Two points. 570 - Arithmetic. 571 """
A. CÓDIGO DEL DSS
573 newPopulation =
Population(population.size(),self.chromosomeLength)
,→
574 for populationIndex in range(population.size()): 575 parent1 = population.getFittest(populationIndex) 576 if self.crossoverRate > np.random.random() and
populationIndex >= \ ,→ 577 self.elitismCount: 578 offspring = Individual(parent1.getChromosomeLength()) ,→
579 parent2 = self.selectParent(population)
580 for geneIndex in range(parent1.getChromosomeLength()): ,→ 581 if 0.5 > np.random.random(): 582 offspring.setGene(geneIndex, parent1.\ 583 getGene(geneIndex)) 584 else: 585 offspring.setGene(geneIndex, parent2.\ 586 getGene(geneIndex)) 587 newPopulation.setIndividual(populationIndex, offspring) ,→ 588 else: 589 newPopulation.setIndividual(populationIndex, parent1) ,→ 590 return newPopulation 591 """
592 This method, called in crossoverPopulation(population),
selects an
,→
593 individual randomly. 594 """
595 def selectParent(self, population):
596 individuals = population.getIndividuals()
A. CÓDIGO DEL DSS
598 rouletteWheelPosition =
np.random.random()*populationFitness
,→
599 spinWheel = 0
600 for individual in individuals:
601 spinWheel += individual.getFitness() 602 if spinWheel >= rouletteWheelPosition:
603 return individual
604 return individuals[population.size()-1] 605 """
606 Given the mutation rate some genes are modified for an
individual.
,→
607 """
608 def mutatePopulation(self, population):
609 newPopulation = Population(self.populationSize,self.ch⌋ romosomeLength)
,→
610 for populationIndex in range(population.size()):
611 individual = population.getFittest(populationIndex)
612 for geneIndex in
range(individual.getChromosomeLength()):
,→
613 if populationIndex > self.elitismCount: