Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
—————
Salti Condizionati
Cicli Iterativi
Functions e Subfunctions
—————
Appunti per l’insegnamento di
Metodi Computazionali per la Finanza
Anno Accademico 2017/2018
Riccardo Cambini
Dipartimento di Economia e Management
Università di Pisa,
Via Cosimo Ridolfi 10, 56124 Pisa, ITALY
E-mail: riccardo.cambini@unipi.it
1
Salti Condizionati e Cicli Iterativi
(Live Script realizzato con MATLAB versione 2018a)
if condizione_vero/falso
Lista_Comandi;
end
Si osservi come il comando "if" debba essere chiuso da un "end", e come l'editor di MATLAB riconosca il
comando e lo evidenzi in colore blu. E' possibile aggiungere alcune opzioni al comando "if":
if condizione_vero/falso
Lista_Comandi_1;
else
Lista_Comandi_2;
end
Questo comando verifica preliminarmente se la "condizione_vero/falso" è vera oppure falsa; nel caso
in cui la condizione sia vera viene eseguita la "Lista_Comandi_1", se è invece falsa viene eseguita la
"Lista_Comandi_2". Si capisce meglio adesso perché questi comandi sono chiamati "salti condizionati", a
seconda infatti della "condizione_vero/falso" si salta ad eseguire una parte di codice ("Lista_Comandi_1")
oppure un'altra ("Lista_Comandi_2") e poi si salta alla fine del comando "if" (ovvero al "end"). Il salto
condizionato "if" si può ulteriormente generalizzare:
if condizione_vero/falso_1
Lista_Comandi_1;
elseif condizione_vero/falso_2
Lista_Comandi_2;
elseif condizione_vero/falso_3
Lista_Comandi_3;
else
Lista_Comandi_4;
end
1
Si osservi che una sola delle 4 "Liste Comandi" viene eseguita, si osservi inoltre che quando una "condizione
vero/falso" è vera non si vanno a verificare le condizioni logiche successive, si esegue la lista comandi
corrispondente e si salta poi alla fine del comando "if" (ovvero al "end").
p = -1
for i=1:n
Lista_Comandi;
end
Si osservi come il comando "for" debba essere chiuso da un "end", e come l'editor di MATLAB riconosca
il comando e lo evidenzi in colore blu. Questo comando continua ad eseguire la "Lista_Comandi",
incrementando ogni volta l'indice "i", fintanto che "i" è minore o uguale ad "n"; quando "i" supera "n" si esce
dal ciclo continuando con i comandi dopo la "end". Nello specifico accade quanto segue:
Si eseguono n iterazioni, concludendo il comando quando "i" prende il valore n+1 e quindi si salta al "end"
finale.
E' possibile anche variare ad ogni iterazione l'indice "i" di un valore diverso da 1. Ad esempio il seguente
ciclo for incrementa ogni volta l'indice "i" di un valore 5:
for i=1:5:n
Lista_Comandi;
end
mentre il seguente "ciclo for" decrementa l'indice "i" di uno procedendo con una sorta di "conto alla rovescia":
2
for i=n:-1:1
Lista_Comandi;
end
p = 4
p = 6
p = 8
p = 11
p = 14
p = 17
p = 3
p = 2.5000
p = 2
Da quanto detto, il numero di iterazioni che vengono eseguite da un "ciclo for" sono determinate a priori. In
realtà è possibile modificare il flusso dei comandi di un ciclo iterativo tramite i comandi "continue" e "break".
for i=1:n
Lista_Comandi_1;
if condizione_vero/falso
continue
end
Lista_Comandi_2;
end
Il comando "continue" chiude anticipatamente l'iterazione corrente andando direttamente alla iterazione
successiva. Nelle iterazioni del comando precedente si esegue prima la "Lista_Comandi_1", si valuta poi la
"condizione_vero/falso", se tale condizione è falsa si esegue la "Lista_Comandi_2", se invece tale condizione
è vera si salta la "Lista_Comandi_2" e si va direttamente alla iterazione successiva. Ne consegue che la
"Lista_Comandi_1" viene eseguita "n" volte, la "Lista_Comandi_2" viene eseguita solamente le volte in cui la
"condizione_vero/falso" è falsa.
for i=1:n
Lista_Comandi_1;
if condizione_vero/falso
break
end
3
Lista_Comandi_2;
end
Il comando "break" chiude anticipatamente l'intero "ciclo for" andando direttamente dopo al "end" finale. Nelle
iterazioni del comando precedente si esegue prima la "Lista_Comandi_1", si valuta poi la "condizione_vero/
falso", se tale condizione è falsa si esegue la "Lista_Comandi_2", se invece tale condizione è vera si
interrompe istantaneamente il "ciclo for" andando al comando successivo al "end" finale.
p = 4
p = 22
p = 6
p = 8
p = 24
p = 4
p = 22
p = 6
while condizione_vero/falso
Lista_Comandi;
end
continua ad eseguire "Lista_Comandi" fintanto che la "condizione_vero/falso" è vera. Nello specifico accade
quanto segue:
• si verifica la "condizione_vero/falso";
• se è vera si esegue la "Lista_Comandi", altrimenti si salta al "end" finale;
4
• si torna all'inizio del ciclo;
Si osservi come non si sappia quante iterazioni verranno eseguite, il comando sarà concluso solamente
quando la "condizione_vero/falso" diventerà falsa. In particolare, un ciclo iterativo "while" potrebbe anche
svolgere infiniti cicli e diventare quindi un "ciclo infinito"; in questo caso il computer continua a lavorare fino a
che non viene privato di energia elettrica, oppure fino a che non vengono premuti conteporaneamente e per
alcuni secondi i tasti "ctrl" e "C".
Si osservi come un ciclo "for" possa essere facilmente scritto sotto forma di ciclo "while":
% è equivalente a
i=1;
while i<=n
Lista_Comandi;
i=i+1;
end
Con qualche abuso computazionale anche un ciclo "while" si può riscrivere tramite un ciclo "for":
% è equivalente a
for i=1:+inf
if condizione_vero/falso
Lista_Comandi;
else
break;
end
end
Come si osserva facilmente, i due cicli iterativi hanno ciascuno le proprie peculiarità e quindi non è opportuno
utilizzare l'uno in termini dell'altro. vediamo di seguito un semplice esempio:
p = 2
p = 4
p = 8
p = 16
p = 32
Come si vede facilmente, la prima volta che "p" supera 20 si esce dal ciclo.
5
Come già si è mostrato con il ciclo "for", all'interno di un ciclo "while" si possono inserire salti condizionati,
altri cicli, oltre ai comandi "continue" e "break".
Functions e Subfunctions
Molti sono i comandi MATLAB che siamo abituati ad utilizzare, come ad esempio "size()", "length()", "zeros()"
etc etc. Ciascuno di questi comandi ha la proprietà di ricevere dei parametri in ingresso, detti inputs, e di
restituire dei risultati in uscita, detti outputs.
Per utilizzare efficientemente MATLAB talvolta si ha la necessità di definire dei nuovi comandi personalizzati
e scritti ad hoc per svolgere determinate funzioni. A tal fine si devono definire delle "functions", ovvero
costrutti aventi ad esempio una struttura del tipo seguente:
function [output1,output2]=NomeFunction(input1,input2)
Lista_Comandi;
end
Una function può avere uno, nessuno, o più input, e può essere scritta in modo tale da restituire uno,
nessuno o più output. Ad esempio:
Una function, per poter diventare un nuovo comando sempre disponibile, deve essere scritta in un m-file
(ovvero in uno script) ed essere salvata nella "Current Folder". Il nome dell'm-file deve essere lo stesso della
function, in caso contrario MATLAB presenta un "warning" per avvertire della possibilità che l'utente possa
eseguire errroneamente una function al posto di un'altra. Ad esempio la seguente function:
function y=Doppio(x)
y=2*x;
end
deve essere redatta in uno script e salvata nella "Current Folder" in un m-file dal nome "Doppio.m". Il nuovo
"comando" può quindi essere invocato facilmente come tutti gli altri comandi MATLAB predefiniti:
Doppio(4)
ans = 8
c=Doppio(2)
c = 4
6
Quando si invoca il comando "Doppio(2)" MATLAB cerca nella "Current Folder" un file denominato
"Doppio.m", passa il valore 2 all'input, esegue i comandi presenti nel corpo della function, e restituisce
l'output. Da questa descrizione si capisce chiaramente (per evitare errori grossolani) sia l'opportunità di
chiamare l'm-file con lo stesso nome della function sia l'opportunità di scrivere una sola function in ogni
singolo m-file.
function euro=Conversione(Valuta,Cambio)
% conversione(Valuta,Cambio) calcola la quantità di euro equivalenti
% alla quantità di valuta straniera "Valuta" al cambio corrente "Cambio"
euro=Valuta/Cambio;
end
Una volta salvata questa function nel file "Conversione.m" la si può utilizzare con i seguenti comandi:
USD=100
USD = 100
TassoCambio=1.0593
TassoCambio = 1.0593
EUR=Conversione(USD,TassoCambio)
EUR = 94.4020
Si osservi come i nomi utilizzati dalla function al proprio interno per gli inputs e gli outputs siano diversi da
quelli utilizzati al suo esterno, ovvero nel presente Live Script, per invocarla.
Le variabili usate dalla function al proprio interno sono chiamate "variabili locali" e non sono visibili/
utilizzabili al suo esterno dal momento che non vengono a far parte del "Workspace".
EUR=Conversione(USD,TassoCambio)
le variabili "USD" e "TassoCambio" vengono prese dal "Workspace", trasformate in valori e passate ai
due inputs della function "Conversione.m". A questo punto l'esecuzione passa alla function, che riceve i
due valori in input e li assegna alle proprie due variabili locali "Valuta" e "Cambio", calcola il valore in euro
assegnandolo alla variabile locale "euro" che poi restituisce come output, tale output viene trasformato in un
valore e restituito come risultato del lavoro della function. La function, una volta terminato il proprio lavoro
viene abbandonata, l'esecuzione dei comandi torna quindi al comando iniziale, il valore fornito dalla function
come risultato viene assegnato alla variabile "EUR" che entra a far parte del "Workspace". Si osservi che
le variabili "Valuta", "Cambio" ed "euro" non compaiono mai nel "Workspace", ovvero esistono in modo
7
trasparente solo durante l'esecuzione della function e svaniscono quando la function ha terminato il proprio
lavoro.
Si osservi infine che per semplificare la scrittura del codice talvolta anche all'interno di una function potrebbe
servire un comando specifico da definire ex-novo. Se tale nuova function è di utilità generale conviene
scriverla in un nuovo m-file, se invece è specifica della function principale si può scrivere direttamente al suo
interno:
function [output1,output2]=FunctionMadre(input1,input2)
Lista_Comandi_Madre;
function out=FunctionFiglia(in)
Lista_Comandi_Figlia
end
end
8
Salti Condizionati e Cicli Iterativi - Esempi Vari
(Live Script realizzato con MATLAB versione 2018a)
Per poter svolgere gli esercizi successivi è opportuno avere dei dati nel Workspace da poter utilizzare. A tal
fine, definiamo il seguente vettore v, la seguente matrice A e calcoliamone le dimensioni:
v=[1 2 -1 -3 2 -1 0 4 0 3 -2 4 0 -2 0]
v = 1×15
1 2 -1 -3 2 -1 0 4 0 3 -2 4 0
n=length(v)
n = 15
A=[1 2 -1 -3 0; 2 -1 0 1 4; 0 -1 3 -2 2; 0 -2 0 -1 3]
A = 4×5
1 2 -1 -3 0
2 -1 0 1 4
0 -1 3 -2 2
0 -2 0 -1 3
[rA,cA]=size(A)
rA = 4
cA = 5
Contatori
Spesso ci si trova a dover contare il numero di volte in cui viene verificata una certa proprietà; si potrebbe
voler contare il numero di monete da due euro che abbiamo in tasca, si potrebbero voler contare il numero
dei flussi di cassa positivi che abbiamo avuto a bilancio. Ad esempio, supponiamo di volerne determinare il
numero degli elementi positivi contenuti nel vettore v. A tal fine due sono i compiti che devono essere svolti:
• occorre visitare ogni singola componente del vettore, dalla prima all'ultima;
• nel caso in cui la componente sia positiva occorre tenerne di conto e "contarla".
Per visitare l'intero vettore occorre un ciclo "for", con la variabile indice del ciclo che passa dal valore 1 al
valore n. Per verificare se la singola componente del vettore è positiva occorre un comando "if". Per contare
i valori positivi occorre una variabile, detta contatore, che viene incrementata ogni volta che si incontra
un valore positivo; ovviamente, la variabile contatore deve essere inizializzata a zero per poter contare
correttamente. Il codice risulta quindi essere:
1
numpos=numpos+1; % in tal caso si incrementa il contatore numpos
end
end
numpos % vediamo quante sono le componenti positive
numpos = 6
Utilizzando anche le opzioni "elseif" ed "else" del salto condizionato "if" è possibile utilizzare anche più
contatori contemporaneamente. Ad esempio, proviamo a contare anche il numero delle componenti negative
e di quelle nulle:
numpos = 6
numneg = 5
numzero = 4
E' possibile utilizzare i contatori anche per analizzare una matrice, ad esempio per contare il numero di
elementi positivi e negativi in ogni singola riga, colonna, o in tutta la matrice. Iniziamo a contare positivi e
negativi in ogni singola riga:
2
ans = 1×4
2 3 2 1
ans = 1×4
2 1 2 2
numpos = 1×5
2 1 1 1 3
numneg = 1×5
0 3 1 3 0
numpos = 8
numneg = 7
3
Accumulatori
Altra necessità estremamente comune è quella di dover sommare tra loro alcuni valori; sommare il valore
delle banconote presenti nel portafoglio per sapere quanti soldi si hanno, sommare il valore dei flussi di
cassa positivi per conoscere l'ammontare complessivo delle entrate a bilancio. Ad esempio, supponiamo di
voler sommare tra loro gli elementi positivi contenuti nel vettore v. A tal fine due sono i compiti che devono
essere svolti:
• occorre visitare ogni singola componente del vettore, dalla prima all'ultima;
• nel caso in cui la componente sia positiva occorre sommarne il valore alla somma parziale.
Per visitare l'intero vettore occorre un ciclo "for", con la variabile indice del ciclo che passa dal valore 1
al valore n. Per verificare se la singola componente del vettore è positiva occorre un comando "if". Per
sommare tra loro i valori positivi occorre una variabile, detta accumulatore, che ogni volta che si incontra
un valore positivo viene aumentata di tale valore; ovviamente, la variabile accumulatore deve essere
inizializzata a zero per poter sommare correttamente. Il codice risulta quindi essere:
valpos = 16
Utilizzando anche l'opzioni "elseif" del salto condizionato "if" è possibile utilizzare anche più accumulatori
contemporaneamente. Ad esempio, proviamo a sommare tra loro anche le componenti negative di v:
valpos = 16
valneg = -9
Si osservi come la differenza tra contatori e accumulatori sia che nei primi si aumenta la variabile di 1 mentre
nei secondi si aumenta la variabile del valore "v(i)".
4
Anche in questo caso è possibile utilizzare gli accumulatori per analizzare una matrice, ad esempio per
sommare il numero di elementi positivi e negativi in ogni singola riga, colonna, o in tutta la matrice. Iniziamo
a sommare positivi e negativi in ogni singola riga:
ans = 1×4
3 7 5 3
ans = 1×4
-4 -1 -3 -3
valpos = 1×5
3 2 3 1 9
valneg = 1×5
0 -4 -1 -6 0
5
for j=1:cA % si scorrono tutte le colonne
if A(i,j)>0 % si verifica se la singola componente è positiva
valpos=valpos+A(i,j); % in tal caso si aumenta l'accumulatore valpos
elseif A(i,j)<0 % si verifica se la singola componente è negativa
valneg=valneg+A(i,j); % in tal caso si aumenta l'accumulatore valneg
end
end
end
valpos % vediamo quanto è la somma delle componenti positive
valpos = 18
valneg = -11
• occorre visitare ogni singola componente del vettore, dalla prima all'ultima;
• occorre memorizzare il massimo/minimo valore temporaneo corrente;
• se si trova un valore migliore (più grande o più piccolo del massimo/minimo valore temporaneo
corrente) si aggiorna l'ottimo temporaneo.
Per visitare l'intero vettore occorre un ciclo "for", con la variabile indice del ciclo che passa dal valore 1 al
valore n. Per verificare se la singola componente del vettore è migliore del massimo/minimo temporaneo
occorre un comando "if". Una prima possibile implementazione potrebbe essere la seguente:
ans = 1×2
-3 4
Si osservi che con questa implementazione si scorre inutilmente il vettore v per due volte. In effetti, la ricerca
del massimo ed il minimo valore poteva essere condotta contemporaneamente:
ans = 1×2
-3 4
Anche questa nuova implementazione può essere migliorata, infatti nel caso in cui sia "v(i)>valmax" è
assolutamente inutile controllare anche se "v(i)<valmin" poiché essendo valmin<=valmax tale condizione
logica è sicuramente falsa. Il codice pertanto si può ulteriormente semplificare utilizzando l'opzione "elseif":
ans = 1×2
-3 4
function [valmin,valmax]=MinMax(v)
valmin=v(1);
valmax=v(1);
for h=2:length(v)
if v(h)>valmax
valmax=v(h);
elseif v(h)<valmin
valmin=v(h);
end
end
end
Questa function, salvata in un opportuno m-file, può essere ad esempio invocata con il seguente comando:
[valmin,valmax]=MinMax(v)
valmin = -3
valmax = 4
7
Si tenga inoltre conto che molto spesso non solo è necessario conoscere i valori massimo e minimo, ma
anche la loro posizione (supponendo sia unica) nel vettore. Per implementare tale ulteriore esigenza le
modifiche da apportare al codice sono minime:
ans = 1×2
-3 4
[posmin posmax]
ans = 1×2
4 8
Ulteriore esigenza, che capita spessissimo quando si lavora con vettori molto lunghi, è quella di voler trovare
i valori massimo e minimo non di tutto il vettore ma di una sua porzione. Supponiamo, ad esempio, di dover
trovare la massima ed la minima componente del vettore v comprese tra la posizione pstart e la posizione
pend, con 1<=pstart<pend<=n. Il codice risulta:
pstart=6;
pend=11;
valmax=v(pstart); % prendiamo l'elemento in posizione pstart come valore max temp.
posmax=pstart; % e se ne memorizza la posizione pstart
valmin=v(pstart); % prendiamo l'elemento in posizione pstart come valore min temp.
posmin=pstart; % e se ne memorizza la posizione pstart
for i=(pstart+1):pend % scorriamo le altre componenti del vettore, da pstart+1 a pend
if v(i)>valmax % se si trova un valore più grande
valmax=v(i); % lo si prende come nuovo valore massimo temporaneo
posmax=i; % e se ne memorizza la posizione i
elseif v(i)<valmin % altrimenti se si trova un valore più piccolo
valmin=v(i); % lo si prende come nuovo valore minimo temporaneo
posmin=i; % e se ne memorizza la posizione i
end
end
[valmin valmax]
ans = 1×2
-2 4
8
[posmin posmax]
ans = 1×2
11 8
Questo codice è facilmente implementabile anche in una function o subfunction(in cui per brevità si assume
sia 1<=pstart<pend<=length(v)):
function [valmin,valmax,posmin,posmax]=MinMaxPos(v,pstart,pend)
valmin=v(pstart);
posmin=pstart;
valmax=v(pstart);
posmax=pstart;
for h=(pstart+1):pend
if v(h)>valmax
valmax=v(h);
posmax=h;
elseif v(h)<valmin
valmin=v(h);
posmin=h;
end
end
end
La precedente function, salvata in un opportuno m-file, può essere ad esempio invocata con il seguente
comando:
[valmin,valmax,posmin,posmax]=MinMaxPos(v,6,11)
valmin = -2
valmax = 4
posmin = 11
posmax = 8
9
end
valmax'
ans = 1×4
2 4 3 3
posmax'
ans = 1×4
2 5 3 5
valmin'
ans = 1×4
-3 -1 -2 -2
posmin'
ans = 1×4
4 2 4 2
valmax = 1×5
2 2 3 1 4
posmax
posmax = 1×5
2 1 3 2 2
valmin
valmin = 1×5
0 -2 -1 -3 0
posmin
10
posmin = 1×5
3 4 1 1 1
Infine, determiniamo massimo e minimo valore di tutta la matrice. In questo caso, per memorizzare la
posizione dobbiamo tener conto sia della riga sia della colonna.
valmax = 4
[rigamax colmax]
ans = 1×2
2 5
valmin
valmin = -3
[rigamin colmin]
ans = 1×2
1 4
Flag
Con il termine "flag" si intendono delle variabili "vero/falso" che indicano se una certa proprietà è o meno
verificata. Ad esempio si potrebbe voler sapere se il vettore "v" ha o meno almeno una componente positiva:
pospresente = logical
1
La variabile flag "pospresente" si comporta un pò come la bandierina dei guardalinee di una partita di calcio,
viene tenuta rivolta verso il basso (valore "false", ovvero non abbiamo ancora trovato alcuna componente
positiva) ed alzata solo in caso di infrazioni di gioco (valore "true", una componente positiva è stata trovata).
Si osservi come la variabile flag debba necessariamente essere inizializzata a "false"; nel ciclo "for" infatti
si può solo riconoscere la presenza di valori positivi ed assegnare alla variabile flag il valore "true", nessun
valore viene invece assegnato nel caso in cui il vettore non abbia alcun valore positivo.
Liste e Maschere
In molte situazioni ci si trova a dover determinare l'elenco delle componenti di un vettore che verificano una
certa proprietà. Ad esempio, l'elenco delle componenti positive, delle componenti negative, dei massimi
o minimi valori nel caso in cui essi non siano unici. Le componenti del vettore che verificano la proprietà
richiesta possono essere espresse in due modi diversi:
• tramite una lista, ovvero un elenco delle componenti che verificano la proprietà richiesta
• tramite una maschera, ovvero un vettore di valori logici vero/falso che indicano se la singola
componente verifica o meno la proprietà richiesta (sostanzialmente un vettore di variabili flag)
Si osservi che la lista non ha una lunghezza predefinita a priori, può essere anche una lista vuota (nessun
elemento verifica la proprietà) ed al massimo è lunga tanto quanto il vettore stesso (tutti gli elementi
verificano la proprietà). Una maschera invece ha sempre la stessa lunghezza del vettore, in quanto ogni
sua componente deve indicare se la corrispondente componente del vettore verifica o meno la proprietà
richiesta.
A titolo di esempio vediamo di individuare tutte le componenti negative del vettore v. Determiniamo prima la
lista:
ListaNeg = 1×5
3 4 6 11 14
e poi la maschera:
12
MaskNeg=false(1,n); % si inizializza la maschera con tutti valori "false"
for i=1:n % si scorre tutto il vettore
if v(i)<0 % si controlla se la componente è negativa
MaskNeg(i)=true; % in tal caso nella maschera si mette il valore "true"
end
end
MaskNeg
Se si lavora con le matrici, le maschere sono a loro volta matrici. Ad esempio la maschera degli elementi
negativi della matrice A risulta:
MaskNeg=false(rA,cA);
for i=1:rA
for j=1:cA
if A(i,j)<0
MaskNeg(i,j)=true;
end
end
end
MaskNeg
la lista stavolta diventa una sequenza di coppie ordinate, quindi una matrice con due righe, in quanto si
devono memorizzare sia le righe sia le colonne degli elementi negativi.
ListaNeg = 2×7
1 1 2 3 3 4 4
3 4 2 2 4 2 4
Metodo di Bisezione
13
Il metodo di bisezione è sicuramente il più semplice algoritmo per determinare gli zeri o le soluzioni di una
equazione.
Si consideri una equazione del tipo , supponiamo che la funzione sia continua e che esistano
due valori a e b tali per cui .
Per la continuità della funzione ed il "Teorema degli Zeri" esiste almeno un valore tale per cui . Il
problema è che raramente è possibile determinare analiticamente la soluzione di una equazione nonlineare;
in questi casi occorre quindi un algoritmo che permetta di fornire una approssimazione di . L'idea
dell'algoritmo è molto semplice:
•
con il valore si divide in due parti uguali l'intervallo
• si calcola il valore
• si prende come nuovo intervallo di
riferimento se oppure se
• si ripete il procedimento fino a che l'intervallo ottenuto non diventa sufficientemente piccolo
Per implementare il metodo di bisezione occorre utilizzare un ciclo iterativo "while", in quanto il numero di
iterazioni da eseguire non è a priori determinato.
A titolo di esempio, cerchiamo uno zero della funzione nell'intervallo con una
precisione di 7 cifre decimali.
A tal fine, definiamo preliminarmente la funzione, gli estremi dell'intervallo e la precisione richiesta.
ans = -1.3891
14
sol = 1.3191
f(sol)
ans = 3.2610e-08
function sol=bisezione(f,xleft,xright,precis)
if not(xl<xr)
error('intervallo non corretto')
end
a=xleft;
b=xright;
fa=f(a);
fb=f(b);
if fa*fb>0
error('Metodo non applicabile')
elseif fa==0
sol=a;
return
elseif fb==0
sol=b;
return
end
while (b-a)>precis
c=(a+b)/2;
fc=f(c);
if fc==0
a=c;
b=c;
elseif fa*fc<0
b=c;
else
a=c;
end
end
sol=(a+b)/2;
end
La precedente function, salvata in un opportuno m-file, può essere ad esempio invocata con il seguente
comando:
sol=bisezione(f,0,2,10^(-7))
sol = 1.3191
f(sol)
ans = 3.2610e-08
15
Salti Condizionati e Cicli Iterativi - Esempi in Analisi Tecnica
Per poter svolgere gli esercizi successivi è opportuno avere dei dati storici nel Workspace. A tal fine
carichiamo preliminarmente il file IBM.csv.
Warning: Variable names were modified to make them valid MATLAB identifiers. The original names are saved in
the VariableDescriptions property.
nday = 14154
ncol = 7
for i=2:nday % scorro le righe dalla seconda per non avere errore con i-1
if not(Ibm.Date(i)>Ibm.Date(i-1)) % verifico il corretto ordine
error('Quotazioni non in ordine cronologico') % se non è corretto si da errorre
end
end
16
Se anche per una sola riga l'ordine non è corretto si ha un errore. Se invece l'ordine delle giornate è corretto
non si restituisce alcun risultato.
Ad esempio, vediamo come individuare nella tabella di cui al file IBM.csv le righe relative al periodo
temporale che va da d1 a d2 con:
• d1 : 25 dicembre 1980
• d2 : 1 maggio 1981
Attenzione, nel codice precedente si assume che sia d1<d2 e che le date siano in ordine cronologico
crescente.
[rowd1 rowd2]
ans = 1×2
4763 4850
[Giorni(rowd1) Giorni(rowd2)]
17
Come si vede, il giorno 25 dicembre 1980 la borsa era chiusa e si è quindi partiti dalla riga corrispondente
alla prima data utile successiva. Il 1 maggio 1981 invece la borsa è risultata aperta.
function [rowdstart,rowdend]=FindRows(days,dstart,dend)
nday=length(days);
for h=1:nday
if days(h)>=dstart
rowdstart=h;
break
end
end
if dend>=days(nday)
rowdend=nday;
else
for h=rowdstart:nday
if days(h)>dend
rowdend=h-1;
break
end
end
end
end
d1=datetime('25-dec-1980'); % si definisce d1
d2=datetime('1-may-1981'); % si definisce d2
[rowd1,rowd2]=FindRows(Ibm.Date,d1,d2)
rowd1 = 4763
rowd2 = 4850
ans = 1×2
4.0800 215.8000
18
Si osservi che si poteva anche utilizzare la function "MaxMin()" precedentemente salvata in un m-file:
[MinVal,MaxVal]=MinMax(Ibm.Close)
MinVal = 4.0800
MaxVal = 215.8000
ans = 1×2
14.6562 17.8750
[PosMinVal PosMaxVal]
ans = 1×2
4849 4769
[Ibm.Date(PosMinVal) Ibm.Date(PosMaxVal)]
Si osservi che si poteva anche utilizzare la function "MaxMinPos()" precedentemente salvata in un m-file:
[MinVal,MaxVal,PosMinVal,PosMaxVal]=MinMaxPos(Ibm.Close,rowd1,rowd2);
[MinVal MaxVal]
ans = 1×2
14.6562 17.8750
[PosMinVal PosMaxVal]
ans = 1×2
4849 4769
19
[Ibm.Date(PosMinVal) Ibm.Date(PosMaxVal)]
Contatori
Supponiamo adesso di voler analizzare i prezzi di chiusura del titolo Ibm andando a contare quante sono
state le giornate in cui si è avuto un rialzo del prezzo, quante sono state le giornate in cui si è avuto un
ribasso del prezzo, quante sono state le giornate in cui si è avuto un rialzo percentuale della quotazione
superiore od uguale al 10%. Sostanzialmente, dobbiamo utilizzare tre contatori ed analizzare i dati storici
nella loro completezza.
numrialzi = 6905
numribassi
numribassi = 6872
numrialzi10
numrialzi10 = 12
Accumulatori
Per studiare l'andamento tendenziale (ovvero il cosiddetto "Trend") delle quotazioni di un titolo di borsa di
solito si calcola la media delle quotazioni degli ultimi 5-10-15 giorni, in modo tale da "smussare" i picchi
speculativi ed avere un andamento più "morbido". Per calcolare queste medie occorre utilizzare degli
accumulatori. Di seguito indichiamo con "p" il numero delle ultime quotazioni consecutive di cui calcolare la
media.
20
for i=p:nday % si scorrono le quotazioni dalla p-esima
accum=0; % si inizializza l'accumulatore a zero
for h=1:p % si scorrono le p quotazioni da sommare
accum=accum+Ibm.Close(i-h+1); % si aggiorna l'accumulatore
end
medie(i)=accum/p; % si calcola la media delle p quotazioni
end
Ovviamente, evitiamo di visualizzare il vettore delle medie avendo esso 14154 componenti.
Flag
Vediamo ad esempio se il titolo Ibm ha avuto o meno una variazione percentuale dei prezzi di chiusura
superiore od uguale al 15%.
trovata = logical
0
Liste e Maschere
Supponiamo adesso di volere la lista delle giornate in cui si è avuto un rialzo percentuale dei prezzi di
chiusura superiore od uguale al 10%.
ans = 1×12
3269 6486 8131 8193 8702 9021
Ibm.Date(listarialzi10)'
21
ans = 1×12 datetime array
1975-01-28 1987-10-20 1994-04-21 1994-07-21 1996-07-25 1997-10-28 1999-04-22 2001-01-03 2001-01-
E' possibile anche determinare la corrispondente maschera, ma risulterebbe impossibile da visualizzare dato
che avrebbe 14154 componenti.
Per comodità, separiamo le date dalle quotazioni e memorizziamo il tutto in due array.
Determiniamo adesso il titolo che ha avuto la massima variazione percentuale e la data in cui ha avuto tale
variazione.
22
end
MaxVarPerc
MaxVarPerc = 0.3186
TitMaxVarPerc
TitMaxVarPerc = 15
DayMaxVarPerc
DayMaxVarPerc = 244
ans =
'MS'
FTSEMIB.Date(DayMaxVarPerc)
ans = datetime
2016-12-13
Sempre a titolo di esempio, determiniamo il titolo che ha avuto il massimo ribasso percentuale e la data in cui
ha avuto tale ribasso. Si ricordi che in questo caso occorre tener conto del segno negativo della variazione
percentuale e che si devono considerare solo i ribassi.
MaxRibPerc = 0.2529
TitMaxRibPerc
TitMaxRibPerc = 21
DayMaxRibPerc
DayMaxRibPerc = 26
23
FTSEMIB.Properties.VariableNames{TitMaxRibPerc+1} % prima col. di FTSEMIB ha le date
ans =
'SPM'
FTSEMIB.Date(DayMaxRibPerc)
ans = datetime
2016-02-08
Calcoliamo adesso per ogni titolo la massima e la minima variazione percentuale e le date in cui si sono
avute tali variazioni.
MaxVarPerc = 1×29
0.0505 0.1276 0.0913 0.1619 0.0799 0.0812 0.0503 0.0668
DayMaxVarPerc
DayMaxVarPerc = 1×29
558 520 28 133 220 30 51 30 133 337 273 28 239
FTSEMIB.Date(DayMaxVarPerc)'
MinVarPerc
MinVarPerc = 1×29
-0.0909 -0.1375 -0.1505 -0.2461 -0.0814 -0.0914 -0.0551 -0.0919
DayMinVarPerc
24
DayMinVarPerc = 1×29
123 123 123 123 123 29 29 123 123 265 123 123 123
FTSEMIB.Date(DayMinVarPerc)'
Calcoliamo infine per ogni giornata la massima e la minima variazione percentuale ed i titoli che hanno avuto
tali variazioni.
ans = 1×593
-Inf 0.0337 0.0002 0.0253 0.0239 0.0320 0.0701 0.0305
TitMaxVarPerc'
ans = 1×593
0 17 18 20 9 20 14 4 4 19 14 18 23
MinVarPerc'
ans = 1×593
Inf -0.0145 -0.0634 -0.0417 -0.0605 -0.0438 -0.0255 -0.0275
TitMinVarPerc'
ans = 1×593
0 20 3 5 18 15 5 14 10 29 4 28 21
Ovviamente la prima componente dei vettori MaxVarPerc, TitMaxVarPerc, MinVarPerc, TitMinVarPerc, non
ha alcuna utilità poiché il ciclo "for" che scorre le giornate deve partire da i=2 (nel calcolo della variazione
percentuale serve la giornata i-1).
25