Processo aleatorio e Random walk

Un processo aleatorio definisce una successione di variabili casuali. Vengono utilizzate nell’ambito finanziario per modellizzare l’andamento di fenomeni di interesse
come prezzi e quantità scambiate. Quali sono i tipi di processi aleatori? Il caso più semplice di processo aleatorio è rappresentato dalla Trading Strategy (insieme o algoritmo di regole prefissate per vendere o acquistare lotti di un certo strumento finanziario di cui i più importanti sono CME e Globex). Gli strumenti principali sono le azioni (o stock), i Futures, le ETF, le Stock Option e le Future Options. Da un punto di vista della programmazione matematica la trattazione può essere fatta in modo uniforme (la categorizzazione ha più senso in campo economico piuttosto che matematico). Qualunque sia lo strumento finanziario che stiamo studiando può essere visto come un processo aleatorio parametrizzato rispetto al tempo e che ad un certo
istante t assumerà un certo prezzo P(t). Possiamo suddividere i processi in discreti e continui a seconda di come voglio considerare l’andamento del tempo. Supponiamo di aver ideato una Trading Strategy: ora nasce la necessità di testare questa strategia e vedere come si comporta per quanto riguarda profitti e perdite. Per fare ciò possiamo usare dati del passato (si parla quindi di Back Test), dati simulati (e cioè creare un processo aleatorio, generati dal computer) oppure usare il Forward Testing (dati reali ma transazioni simulate). Quando andiamo a simulare una sequenza di prezzi è sempre necessario considerare le due sequenze di prezzo più o meno parallele di Bid e Ask.

Modellizzazione del prezzo

Da un punto di vista pratico il prezzo Ask è quello applicato quando compriamo un certo numero di azioni. Se allo stesso tempo qualcuno venderà il nostro stesso strumento finanziario dovrà tenere in considerazione il prezzo Bid. Il prezzo Spread equivale alla differenza tra prezzo ask e bid (rappresenta la remunerazione del mercato). Altra caratteristica importante è che il prezzo si muove su intervalli definiti (il prezzo varia con salti finiti): si parla quindi di minima variazione di prezzo di uno strumento finanziario con il termine Tick. Ad ogni istante le curve Bid e Ask sono sostanzialmente costituite da gradini pari all’incremento minimo di prezzo che è possibile per quel certo strumento. Per intervalli temporali molto ampi non vedremo più la microstructure ma osserveremo un andamento più curvilineo. In conclusione avremo tempi e prezzi discreti. L’equivalente del Tick (utilizzato per le azioni) è
il Pip (per gli scambi in euro, dollari,..). Quello che ci interessa è la generazione di fenomeni aleatori che possano simulare il fenomeno di interesse come la variazione del prezzo. Per prima cosa dobbiamo considerare tempi discreti e per ogni tempo t devo generare due curve Ask e Bid. queste curve generano uno spread aleatorio e da un certo t al successivo t+1 ci muoveremo ad incrementi fissati (Tick o Pip). Ovviamente i tempi sono relativi alla disponibilità dei dati.

Simulazione della strategia

Il primo passo consiste nel generare il processo aleatorio che rappresenta il nostro prezzo. Su questa serie andremo a plottare gli ordini che dipendono dalle regole
(Trading Strategy) con cui voglio fare questo ordine. In seguito dovremo generare le curve di Ask e Bid da cui deriva lo Spread , mediante l’applicazione della
strategia, potremo calcolare il PnL (profit and loss) e altri parametri di interesse.
Il Pnl è una funzione dei prezzi correnti. Supponiamo di aver comprato una quantità Q al prezzo Pb e di averlo poi venduto ad un tempo successivo al prezzo Ps.
Il Pnl assume dunque valore pari a Q*(Ps-Pb) e può essere positivo (profit) o negativo (loss). Alcuni strumenti finanziari come i Futures hanno anche un altro par che modifica
la perdita e il profitto, il moltiplicatore, che va a moltiplicare la formala standard del Pnl.

Random Walk

Per generare una random walk dobbiamo stabilire dei punti di partenza: supponiamo, quindi di avere uno Stock Exchage o un file csv che ci fornisce un flusso di dati
ma, poiché noi siamo interessati alla generazione, ricorreremo alla creazione dei dati mediante un generatore di un processo aleatorio che a sua volta userà un generatore di numeri pseudo casuali. Questi dati saranno il tempo a cui il dato arriva, un prezzo Bid e Ask. Noi dovremo generare tutti e tre questi elementi tenendo a mente il vincolo del Tick (minimo incremento di prezzo) che potrà essere positivo o negativo. Quindi potremo usare un processo di Bernoulli per determinare questo salto verso l’alto (con probabilità p) o il basso (con probabilità 1-p) quando passo dal tempo t al tempo t+1.

Nozione di ricorsione e di iterazione

Queste due nozioni rispondono alla necessità del programmatore di ripetere delle parti di codice in cicli iterativi con dati via via diversi in ogni ciclo fino all’esecuzione di una condizione di uscita. Nell’iterazione il loop è contenuto nel comando chiamante mentre in quella ricorsiva il loop si verifica mediante una funzione R(x) che al suo interno ha una chiamata a se stessa e ciò innesta ovviamente un loop. Come si interrompe questo loop? In R(x) ci deve essere definito il cosiddetto
Base Case che specifica le condizioni che portano all’uscita del loop

Nozioni di Stack e Heap

Con il termine Stack si intende l’area di memoria destinata a mantenere informazioni sulle funzioni. Lo Stack può essere a sua volta suddiviso in diverse aree di memoria:

  • una prima parte destinata a contenere le istruzioni eseguibili del programma stesso (code)
  • un’altra area destinata invece a contenere variabili globali, definite al di fuori delle funzioni
  • un’area che si occupa dell’allocazione dinamica di oggetti destinati a crescere indefinitivamente (come le reference type) definita HEAP: gli oggetti che vanno qui non hanno una dimensione definita.

Consideriamo un generico programma che parte dalla funzione main per poi andare al gestore evento button click che ha chiamato a sua volta una seconda funzione F(x) la quale ne ha chiamata una terza che indichiamo con G(x): tutte queste chiamate corrispondono ad altrettante zone di memoria riservate nello Stack per ogni funzione: ognuna di esse causa la creazione di un’area relativa alla singola funzione (quest’area
viene detta Stack Frame o Activation Record): nello Stack frame ci sono informazioni tra cui le variabili locali definite all’interno delle singole funzioni (value type), gli eventuali parametri che gli vengono passati e l’indirizzo di ritorno per quando la funzione termina. Supponiamo di essere passati in G(x) che ci riporta in F(x): tutti i dati di G vengono cancellati dallo Stack. Stessa cosa per F quando si ritorna nel Click e così via: possiamo riassumere questo funzionamento mediante l’acronimo LIFO (l’ultimo che entra è anche il primo che esce).
La dimensione dello Stack è pari ad un 1 GB per programmi a 32 bit e 4 GB per 64 bit ma se c’è un eccesso di chiamate si può avere un errore detto Stack Overflow.

PRINCIPALI OGGETTI GRAFICI

I linguaggi di programmazione VB.NET e C# permettono di creare dei grafici struttando oggetti grafici contenuti nella libreria GDI+. Per creare un grafico si devono seguire i seguenti step:

  1. Creazione dell’oggetto bitmap, cioè un’area costituita dai pixel destinata a contenere le immagini. Tale oggetto richiede come input le dimensioni del rettangolo, cioè altezza (h: height) e larghezza (w: weight)
  2. Istanziamento dell’oggetto grafico Graphics che rappresenta una superficie di disegno dalla libreria GDI+. Permette di creare un’immagine da un file passato come argomento. Questo file deve trovarsi nella stessa cartella del file eseguibile dell’applicazione
  3. Realizzazione della parte grafica
  4. Rappresentare il bitmap con un oggetto come una PictureBox e caricare quindi il bitmap come fosse una fotografia
'PASSO 1
Dim b As New Bitmap (500,400)
'PASSO 2
Dim g As Graphics = Grapichs.FromImage(b)
'PASSO 3

'PASSO 4
Me.PictureBox1.Image = b

Al passo 3 possono essere inseriti molti oggetti, tra cui:

  • La classe Pen che permette di creare un oggetto in grado di disegnare linee o curve. Allo stesso tempo però può essere utilizzato direttamente l’oggetto Pens.
  • L’ultimo oggetto appena visto è molto utilizzato con il metodo DrawRectangle. Questo metodo grafico permette di disegnare un rettangolo delle dimensioni che si desiderano. Gli argomenti richiesti sono l’oggetto Pen, che determina il colore, la larghezza e lo stile del rettangolo e la struttura Rectangle che rappresenta il rettangolo da disegnare.
  • Molto utile quando si programma è il metodo grafico Clear. Tale metodo grafico permette di cancellare l’intera area grafica e di colorare tutto lo sfondo con un colore passato in input.
  • Un’entità che svolge un ruolo simile è la classe Brush, che a differenza del metodo Clear, permette di colorare l’area all’interno di una figura.
  • Per disegnare lo scatter plot di una distribuzione bivariata può essere utilizzato il metodo grafico FillEllipse che permette di riempie l’area interna di un’ellisse definita da un rettangolo di delimitazione specificato.
  • Per poter aggiungere le labels a un grafico creato, è possibile utilizzare la classe Font, che definisce un particolare formato per il testo, compresi tipo di carattere, dimensioni e attributi di stile.
  • Un ulteriore modo per poter creare lo scatter plot è l’utilizzo della struttura Point che permette di rappresentare una coppia ordinata di coordinate di valori interi x e y per definire un punto in un piano a due dimensioni.

Il seguente codice permette di rappresentare lo scatter plot utilizzando FillEllipse

g.DrawRectangle(Pens.Blue, New Rectangle(10,10,200,100))
g.Clear(Color.White)
g.FillEllipse(Brushes.OrangeRed, New Rectangle(30,30,5,5))

TRASFORMAZIONE DELLE COORDINATE DA REALE A VIRTUALE

Come abbiamo già visto nel precedente articolo, i linguaggi VB.NET e C# sfruttano una libreria grafica chiamata GDI+ presente al loro interno per realizzare delle rappresentazioni statistiche anche molto elaborate. Il primo ostacolo che si deve superare per sfruttare a pieno le potenzialità di questa libreria però è riuscire a trasformare le coordinate del “mondo reale”, quindi le coordinate del piano cartesiano, in coordinate del “mondo virtuale”, quindi i pixel. Per fare ciò si deve operare un riproporzionamento attraverso una trasformazione lineare. La prima cosa da fare è riuscire a capire le dimensioni della finestra reale, perciò si devono conoscere 4 grandezze: Ymin, Ymax, Xmin, Xmax. Nel caso dell’asse delle ascisse, quindi delle coordinate x, il ragionamento è semplice: posto W la larghezza del rettangolo virtuale e L lo scostamento del rettangolo dal lato sinistro, si ottiene la seguente trasformazione:

Per ottenere una formula valida per la seconda coordinata, quindi per le coordinate dell’asse delle ordinate, si devono prima fare delle considerazioni. A differenza del “mondo reale”, nel “mondo virtuale” l’origine degli assi non è il vertice in basso a sinistra, ma il vertice in alto a sinistra. Se non si tenesse conto di questa differenza, si rischierebbe di creare un grafico capovolto; per questo motivo si deve considerare il complemento all’altezza quando si crea la formula per il calcolo delle coordinate y nel mondo virtuale. Posta H l’altezza del rettangolo virtuale, e T lo scostamento del rettangolo dall’alto, si ottiene la seguente formula:

Queste trasformazioni possono essere scritte in VB.NET nel seguente modo:

Function X_RealeTo_X_Virtuale (MinX As Double, MaxX As Double, Left As Integer, X_Reale As Double, Width As Double) As Integer
   Return CInt(Left + Width *(X_Reale - MinX)/(MaxX - MinX))
End Function


Function Y_RealeTo_Y_Virtuale (MinY As Double, MaxY As Double, TopAs Integer, Y_Reale As Double, Height As Double) As Integer
   Return CInt(Top + Height - Height*(Y_Reale - MinY)/(MaxY - MinY))
End Function

Funzione Lambda

Definiamo con questo nome le funzioni che permettono di rendere più pulito e leggibile un codice: esse, infatti, forniscono scorciatoie per ordinare, filtrare, e lavorare con informazioni presenti negli elenchi. Esse sono chiamate anche “funzioni anonime” poiché non hanno un nome. Vediamo, dunque, in che modo la funzione lambda lavora con gli item di una lista mediante il seguente esempio. Supponiamo di dover considerare la classe Customer definita in questo modo:

public class Customer
    {
        public int CustomerId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EmailAddress { get; set; }
        public bool IsDirty { get; set; }
    }

Normalmente, si otterrebbero i dati dei clienti da una tabella ma ai fini di questo esempio, questo codice crea l’elenco:

List<Customer> custList = new List<Customer>
    {new Customer()
          { CustomerId = 1,
            FirstName="Bilbo",
            LastName = "Baggins",
            EmailAddress = "bb@hob.me"},
    new Customer()
          { CustomerId = 2,
            FirstName="Frodo",
            LastName = "Baggins",
            EmailAddress = "fb@hob.me"},
    new Customer()
          { CustomerId = 3,
            FirstName="Samwise",
            LastName = "Gamgee",
            EmailAddress = "sg@hob.me"},
    new Customer()
          { CustomerId = 4,
            FirstName="Rosie",
            LastName = "Cotton",
            EmailAddress = "rc@hob.me"}};

Ora che abbiamo un elenco, supponiamo di voler trovare il cliente che ha un ID di 4. Potremo raggiungere questo obiettivo usando un ciclo for / each:

Customer foundCustomer = null;
    
foreach (var c in custList)
{
   if (c.CustomerId == 4)
   {
      foundCustomer = c;
      break;
   }
}

Tuttavia, è possibile raggiungere lo stesso risultato in con un minor numero di codici utilizzando la funzione lambda

var foundCustomer = custList.FirstOrDefault(c =>
                        c.CustomerId == 4);

L’operatore “=>” è detto operatore lambda; è utilizzato per separare il parametro, o i parametri, dell’espressione lambda, dall’espressione stessa. E’ importante notare che in questo esempio, il parametro lambda c, che rappresenta tutti i valori nella lista, è un oggetto di tipo “Costumer”, ma questo tipo non è stato specificato. Le espressioni lambda infatti utilizzano la conversione di tipo implicitamente si volesse cambiare il tipo del parametro dell’espressione (cosa solitamente desueta), si può scrivere:

Customer foundCustomer = custList.FirstOrDefault((Customer c) => c.CustomerId == 4);

Il linguaggio che abbiamo utilizzato per definire questa funzione lambda è C#. L’equivalente funzione trascritta utilizzando il linguaggio VB sarà la seguente:

Dim foundCustomer = _
        custList.FirstOrDefault(Function(c) _
                          c.CustomerId = 4)

Il principale svantaggio della funzione lambda è che l’applicazione di questo tipo di espressioni risulta essere di scarsa utilità al di fuori del contesto di definizione.

Procedure per calcolare la distribuzione di frequenze univariata

Nella scorsa lezione abbiamo visto come mediante lo Stream Reader o il Text Field Parse siamo in grado di leggere un file cvs di dati organizzati in unità statistiche di interesse (righe) e caratteri (colonne) e di trasformare questa matrice in una lista di oggetti che contiene quelle osservazioni. Il singolo oggetto di quella lista sarà l’istanza di una classe che rappresenta un’ unità statistica. Adesso vedremo come tradurre questa lista di oggetti in una distribuzione di frequenza. Quindi da un punto di vista concettuale il file csv potrà essere considerato come un dataset multivariato ma, almeno per adesso ci limiteremo ad analizzar una distribuzione univariata per determinare la distribuzione di frequenza: suddivido il dataset in classi distinte e conto quante unità statistiche cadono in ognuna di esse. Questa distribuzione si presenta come una tabella a due colonne in cui nella prima sono presenti le classi (class intervall) e nella seconda avremo le relative frequenze: queste due colonne insieme rappresentano la frequency distribution. In generale potremmo calcolare la distribuzione di frequenza per ogni carattere purché sia sensato (non conviene farlo per caratteri che restituirebbero frequenze unitarie). Per determinare le classi posso indicare il valore minimo assunto dalle unità per il carattere che sto studiando e l’ampiezza delle classi. Per contare
frequenza relativa ad ogni classe ricordiamo che le unità appartengono ad una lista e perciò potrei osservare singolarmente ogni valore per determinare a quale classe
appartiene. Questo è un approccio naive che equivale ad un loop di osservazioni e per ciascuna di esse avrò un loop di classi. Un approccio migliore è il seguente: per prima
cosa ordino le unità e poi, visto che la distribuzione viene costruita da un certo minimo e via via aggiungendo intervalli, le posso esaminare mentre costruisco la distribuzione: quindi via via che costruisco le classi intervallari posso anche contare quante unità statistiche appartengono ad ogni classe e così facendo elimino un loop.

Metodi disponibili per leggere file testuali di dati statistici

Abbiamo già dato la definizione di file csv. Esaminiamo adesso i metodi messi a disposizione dai linguaggi di programmazione VB.Net e C# per leggere questi file

  • Hardcode: Questo metodo, seppure sia il più semplice, non viene usato molto spesso poiché risulta poco elastico e maneggevole. Prevede la creazione di una variabile di tipo stringa: essa sarà il path o percorso del file che ci interessa importare.
// metodo hardcode

Dim NomeFile As String = "C://Statistica_Applicata/Automobili.csv"
  • StreamReader: oggetto specializzato per leggere i fil di testo che possiamo trovare in ogni linguaggio di Visual Studio e che può essere customizzato a piacimento. Proprio perché questo oggetto ci permette di intervenire manualmente (presenta quindi massima flessibilità) è anche soggetto ad errori nell’operazione di lettura. Per ottenere il path del file utilizziamo una “Open File Dialog”.Al passo successivo, che possiamo chiamare passo 2, dobbiamo installare un’oggetto in grado di leggere il path. Dopodiché dovremo poter leggere le righe di testo del file, facendo attenzione alla prima riga, che potrebbe contenere il nome delle variabili (header), e poi estrarre i dati. Questi dati dovranno poi essere memorizzati in appositi oggetti come una lista attraverso l’uso di una classe.
// ottenimento del path con una Open File Dialog

Dim ofd As New OpenFileDialog
ofd.ShowDialog()

//istanziamento del path in un oggetto

Dim sr As New System.IO.StreamReader(ofd.FileName)

//lettura delle righe del file e estrazione dei dati

Dim sr As New StreamReader(ofd.FileName)
Dim RigaH As String = sr.ReadLine()   'in questo modo leggo l'header
While Not sr.EndOfStream
 Dim RigaDati As String = sr.ReadLine()
 Dim Dati() As String = RigaDati.Split(",".ToCharArray,StringSplitOptions.RemoveEmptyEntries)
End While

//all'interno del ciclo inserirò anche una variabile con tipo la classe creata per inserire i dati.

  • TextFieldParse: soluzione messa a disposizione dalla libreria Microsoft Visual Basic. Poiché risulta specializzato nel parsing di file cvs sarà anche più robusto in relazione all’eventuale presenza di file poco comuni e che avrebbero potuto darci problemi se avessimo usato l’oggetto StreamReader: questa particolarità è dovuta alla presenza di più controlli. Se si utilizza il linguaggio Vb.Net non è richiesta l’aggiunta di una referenza, mentre se si usa il linguaggio C# si. La sintassi e i metodi utilizzati non differiscono molto dallo StreamReader; i due differiscono solo dall’inizializzazione del path, per la sostituzione del comando di spalti presente nel ciclo appena visto e per la specificazione dei delimitatori. Per questo motivo vediamo la codifica solo di queste differenze.
//inizializzazione del path

Dim tap As New Microsoft.VisualBasic.FileIO.TextFieldParse(FileName)

// comando sostitutivo dello spalti nel ciclo

tfp.ReadFilds()

//inizializzazione proprietà delimitatori

tfp.SetDelimiters(New String() {","})
tfp.CommentTokens = New String() {"#"}
tfp.HasFieldsEnclodesInQuotes = True

Una menzione speciale va fatta ai alle azione DragDrop e DragEnter. Esse permettono la lettura di un file attraverso il trascinamento della richTextBox, dopo aver abilitato questa possibilità dalle proprietà dello strumento. Vediamo ora la sintassi di questi due gestori

//nel gestore di DragEnter
If e.Data.GetDataPresent(DataFormats.FileDrop) Then e.Effect = DragDropEffects.Copy

//nel gestore DragDrop
e.Effect = DragDropEffects.None
Dim Files() As String = CTyper(e.Data.GetData(DataFormats.FileDrop), String())
For Each path In Files
 Me.RichTextBox1.AppendText(vbCrLf & path)
Next

//per richiamare il file
NomeFile = RichTextBox1.Text

E’ utile anche menzionare il metodo “Dispose()”disponibile sia per StreamReader che per TextFieldParse. Esso permette di rilasciare il file dopo le modifiche, poiché se il file viene danneggiato, altri programmi potrebbero non riuscire ad aprirlo correttamente. Per questo motivo tutte le istruzione dovrebbero andare in un TRY-CATH e il metodo Dispose() nel Finally. Questo metodo di procedere però è molto scomodo, per questo motivo è stata creata la struttura Using, che richiede che l’oggetto abbia un metodo Dispose. La presenza di questa struttura rende superfluo l’utilizzo del TRY-CATCH.

// all'inizio del codice inserisco
Using top As New Microsoft.VisualBasic.FileIO.TestFieldParse(Me.NomeFile)

//alla fine del codice
End Using

Elementi fondamentali della specifica CSV

il csv è un file di testo che presenta la seguente struttura:

  • Ogni riga di questo file deve contenere le informazioni relative ad un certo oggetto e le colonne rappresentano le variabili
  • I separatori utilizzati sono , oppure ;

Come si leggono questi dati programmaticamente per fare le nostre analisi?
I linguaggi di Visual Studio mettono a disposizione oggetti per leggere i file di testo, noi ne vedremo solo due:

  • StreamReader (è più potente ma la sua flessibilità comporta dei rischi, poiché è necessario eseguire manualmente le modifiche)
  • TextFieldParse (dentro ha controlli e funzionalità messe a disposizione da Microsoft)

Il primo è direttamente disponibile nei nostri due linguaggi, il secondo è specifico di vb (la posso usare anche per c# ma devo aggiungere una reference).

La formula di Legendre e la sua utilità nelle applicazioni

Nel precedente articolo abbiamo descritto alcuni metodi basati sugli operatori bitwise che permettono di sfruttare le relazioni tra valori numerici sotto forma di sequenze di bit. In particolare avevamo descritto come si poteva determinare la massima potenza di un generico n. Quello che affronteremo in questo articolo è la risoluzione di un problema molto simile a quello appena ricordato ma che necessita di un procedimento leggermente diverso: determinare la massima potenza che divide il fattoriale di n. In particolare verrà descritta la risoluzione di questo problema assumendo che la base di questa potenza sia 2 ma non temete, alla fine di questo articolo verranno introdotte le versioni generalizzate di questo procedimento. Utilizzeremo nella risoluzione di questo problema la formula di Legendre secondo cui posso scomporre un qualunque numero M per una qualunque potenza per un altro numero:

Figura 1

L’esponente è detto valutazione p-adica di M dove p è il numero che sta alla base dell’esponente:

Figura 2

Facciamo un esempio: consideriamo un numero n pari a 17. Il suo fattoriale si ottiene come prodotto dei numeri interi minori o uguali di sé.

Figura 3

Adesso contiamo i due presenti sulla colonna di destra: sono pari ad 8 (la parte intera di 17/2). Se eliminassimo i 2 appena considerati potremmo ripetere questo procedimento peri termini da 1 a 8.

Figura 4

Questa volta il numero di volte in cui compare il 2 è pari a 4 (e cioè alla parte intera di 17/4). Eseguendo iterativamente questo processo otterremo che il numero totale di volte in cui è comparso il 2 sarà pari a 15. Perciò concludiamo dicendo che

Figura 5

dove R equivale a tutti i numeri dispari che non ho considerato nei vari passaggi.

In termini generali, riprendendo la formula in Figura 2, possiamo affermare che v risulta essere pari a

Figura 6

Nel caso in cui n fosse una potenza di 2 (ad esempio 16) potremmo facilmente considerarla come una somma di altre potenze con la stessa base. In questo caso, eseguendo gli stessi calcoli di prima, otterremo che v risulta essere pari a 15 e cioè al valore di n a cui è stato sottratto il numero di bit pari ad 1 che contiene al suo interno. In conclusione, se non volessimo sfruttare la rappresentazione dualica ma ci volessimo limitare al caso generico potremmo dire che la potenza v risulta essere pari a

Figura 7
Progetta un sito come questo con WordPress.com
Comincia ora