Si fa presto a dire ISC e sul valore del codice commentato

Nell'autunno 2007 fui ingaggiato da UBI Casa per alcune attività tra cui un generatore di documenti finanziari semi-automaico. In questo contesto, si doveva anche dismettere un AS/400 su cui restava una funzione  per il calcolo dell'indice Sintetico di costo (ISC o TAEG), che serviva sia nel mio progetto che per una calcolatore da visualizzare sul sito. Le specifiche erano abbastanza vaghe, e mi fu consegnato il codice simile al seguente:

****************** Inizio dati **************************************** 
H DECEDIT('0,') DATEDIT(*DMY.)                                          
 *+-------------------------------------------------------------------* 
 *+ CLCTAEG - Calcolo taeg                                              
 *+                                                                     
 *+            Parametri input:                                         
 *+                                                                     
 *+             1    MutPres                                            
 *+            15,2  CapitErogato                                       
 *+            15,2  ImportoRata                                        
 *+             3,0  NumeroRate                                         
 *+            15,2  OnerieSpese                                        
 *+             9,7  TassoNominale                                      
 *+            15,2  Preammortamento                                    
 *+             1,0  Periodicita                                        
 *+            15,2  Spese ricorrenti                                   
 *+             2,0  Frequenza per spese ricorrenti 12=annuale, 1=mensi 
*+                                                                     
*+            Parametri output:                                        
*+                                                                     
*+             9,7  Taeg                                               
*+             1    RtnCode                                            
*+             1    Usura (0=NO usura 1=SI Usura 9=Errore)             
*+                                                                     
*+-------------------------------------------------------------------* 
*+                                                                     
*+ Data       15/06/2002                                               
*+ Progr.     XXXXXXXXXXXX                                      
*+                                                                     
*+-------------------------------------------------------------------* 
*+ Manutenzione                                                        
*+ xxxx  xx/xx/xxxx  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
*+-------------------------------------------------------------------* 
                                                                       
Flnp011    IF   E           K DISK    usropn                            
                                                                        
DTaegerr          S             15P 7                                   
DTassoUsura       S              9P 7                                   
DTassoMinimo      S              9P 7                                   
DTassoMassimo     S              9P 7                                   
DDeltaTaeg        S              9P 7                                   
....                           
                                                                      
C     *ENTRY        PLIST                                             
C                   PARM                    MutPres           1       
C                   PARM                    CapitErogato     15 2     
C                   PARM                    ImportoRata      15 2     
...
                                                                      
                                                                        
C                   clear                   Taeg                        
C                   clear                   RtnCode                     
C                   clear                   Usura                       
C                   clear                   Attualizzato                
                                                                        
C     klnp11        klist                                               
C                   kfld                    banca             3 0       
C                   kfld                    indice            2 0       
                                                                        
C                   eval      Tassominimo   = 0                         
C                   eval      TassoMassimo  = 2                         
C                   eval      DeltaTaeg     = 0,1                       
C                   eval      Scostamento   = 0,00000001                
 * se importo rata e tasso uguali a zero errore                         
C                   if        ImportoRata = *zeros                      
C                             and TassoNominale = *zeros                
C                   eval      RtnCode       = 'I'                       
C                   eval      *inlr         = '1'                       
C                   return                                              
C                   endif                                               
 * se importo rata passato a zero lo calcolo                            
C                   if        ImportoRata = *zeros                      
C                             and TassoNominale <> *zeros               
 * se numero rate <= 0 errore                                           
C                   if        NumeroRate = *zeros                       
C                   eval      RtnCode       = 'I'                       
C                   eval      *inlr         = '1'                       
C                   return                                              
C                   endif                                               
                                                                        
C                   call      'CALCFIN'                                 
C                   parm      NumeroRate    nrate             3 0       
C                   parm      *zeros        rata             11 2       
                                                                        
C                   parm      TassoNominale tasso             9 7       
C                   parm      CapitErogato  caper            11 2       
 * se importo rata uguale a zero errore                                 
C                   if        rata = *zeros                             
C                   eval      RtnCode       = 'I'                       
C                   eval      *inlr         = '1'                       
C                   return                                              
C                   endif                                               
C                   eval      ImportoRata   = rata  
....

 

Se non ricordo male il linguaggio con cui era scritto questo programma era il PL/I, un linguaggio compatibile con le schede perforate (!) tanto che il codice è formattato per colonne.

Il codice era stato mantenuto almeno fino al 2002, e difatti ci sono una serie di costi variabili che sono stati resi obsoleti dalla Legge Bersani (tipo le commissioni per rimborso anticipato, ecc). Ora come immaginate non era un compito semplice, soprattutto perché non c'erano specifiche, c'era solo questo codice prossimo all'oblio, ma commentato benissimo.

Le mie interfacce funzionali erano persone che sapevano usare un solo tool: Excel; nulla di male intendiamoci, ma dovevano validarmi i calcoli. Inoltre dovevo lavorarci mentre ero in un altra sede del Gruppo Bancario, dove mi occupavo di altri aspetti.

Per cui iniziai a documentarmi in diverse direzioni:

  1. Recuperai da Wikipedia qualche frammento di codice PL/I Di fatto si trattava di una specie di linguaggio macchina
  2. Mi documentai su come si calcola l'ISC, prendendo spunto da Wikipedia e  confrontando la formula con l'algoritmo
Mi resi conto presto che la funzione che calcola l'ISC non è invertibile: cioé non esiste una formula ANALITICA che dati gli input possa calcolare l'ISC. Il modo più semplice per risolvere questa situazione è applicare la teoria del calcolo numerico, e trovare l'interesse che "spalmato" sulla sommatoria soddisfa l'equazione. Tra i metodi che si possono usare c'è quello di bisezione, che non converge velocemente ma è stabile (cioé piccole variazioni degli input non perturbano il risultato) anche se richiede che la funzione abbia una inversione di segno nell'intervallo considerato, e sia continua. Ovviamente la funzione del calcolo dell'ISC soddisfa tutte queste precondizioni, e infatti dopo un po' di studio, scoprii che il codice originario usava proprio un metodo basato sul metodo di bisezione.

Risolto questo problema ne restavano almeno altri due: come testare "da remoto" l'algoritmo, e come adattarlo alla normativa vigente. Alla fine decisi di creare un foglio Excel, con una macro Visual Basic che consentisse di inputare i parametri e testarne l'output.

Il foglio ebbe enorme successo, e il client manager a più riprese mi chiese di spedirgli il programma anche per altri progetti collegati.

Il commento che precedeva il codice descriveva per sommi capi l'algoritmo:

' Cicla su tutte le rate
' sorvolando sui check per le spese ricorrenti
' questo ciclo e' la sommatoria
' TotaleRata * [ SommeSu (i+1) ^ -s (p/12) ]
' ove s cicla su tutte le rate
' i e' il taeg stimato corrente
' p/12 e' la normalizzazione della periodicita' che vale 12 se e' zero