In [ ]:
<b>ENTREGA: </b>
El dia límit per a l'entrega d'aquesta pràctica és el dia <b>15 de desembre a les 23.55h</b>

Processament de seqüències.

En aquesta pràctica treballarem amb el problema de processament de dades seqüencials. Obtindrem les dades històriques de la borsa de diferents empreses i analitzarem el valor de les seves accions al llarg del temps. Definirem un algoritme que ens permetrà detectar canvis de comportament, i finalment, definirem una estratègia de compra/venta d'accions al detectar canvis de comportament de les dades.

La pràctica consisteix de tres exercicis.

Exercici 1: Implementar l'algoritme de la finestra lliscant</br> Exercici 2: Implementar l'algoritme de la finestra adaptativa (ADWIN)</br> Exercici 3: En aquest últim exercici, volem definir una estratègia de compra/venda d'accions amb l'objectiu de tindre beneficis.</br>

Per poder obtenir aquestes dades de la borsa, utilitzarem un paquet anomenat ystockquote.py. Aquest paquet conté un conjunt de funcions que ens permet obtenir les dades actuals e històriques de qualsevol empresa de la borsa.


In [1]:
#NOTA: Per Linux - executar com --> ipython notebook --pylab=inline

from utilsP4 import *
#%matplotlib inline 

# Carreguem les dades de la borsa mitjaçant la funció loadStockData
data={}
#companies=['GOOG']
#companies=['MSFT']
#companies=['IBM']
companies=['YHOO']
#companies=['FB']
                      
for c in companies:
    data[c]=loadStockData(c)


Exercici 1

La finestra lliscant: Les finestres lliscants són l'eina més comuna per treballar sobre fluxos de dades. Donat que els fluxos de dades són virutalment infinits el que usualment es fa és considerar el conjunt de dades finit de mostres anteriors a la que ens arriba al temps actual.

Exemple: Considerem la finestra lliscant de mida W=8. Observeu que en verd es troben identificades les posicions que és consideren per fer els càlculs en el temps t i blau les posicions que és consideren en el temps t+1.

0.22 0.88 0.21 0.41 0.33 0.41 0.12 0.43 0.38 0.22
t-W+1 t-2 t-1 t
t-W+2 t-1 t t+1

Amb aquesta consideració es pot entendre que realitzar càlculs sobre fluxos de dades té la seva dificultat i pot ser impossible de trobar solucions exactes sense emmagatzemar tot el flux. Tot i això hi ha càlculs que sí es poden realitzar de forma exacta només considerant les dades que ens arriben. Un d'aquests càlculs és el de la mitjana aritmètica. $$ \bar{x} = \frac{1}{W} \sum_{i=t-W+1}^{t}x_i$$

Observeu que en l'exemple anterior la mitjana en el temps t té un valor 0.39625 i la mitjana en el temps t+1 té un valor 0.31375.

Com podeu apreciar a la formula de la mitjana aritmètica, necessiteu guardar tot el flux de dades de la finestra t per el càlcul de la mitjana en el temps t+1. Penseu e implementeu un mètode que ens permeti treure la mitjana en el temps t+1 a partir de la mitjana en el temps t.


In [2]:
class SlidWin:
    """
        Finestra lliscant que permet trobar la mitja d'una sèrie de dades de mida màxima W_size.
        Aquesta finestra lliscant recalcula la mitja amb les fòrmules d'increment i decrement que es troben més abaix d'aquest fitxer.
    """
    def __init__(self, W_size):
        """
            Constructor que inicialitza els paràmetres per la finestra lliscant.
            param W_size Mida màxima de la finestra lliscant.
        """
        self.data = [] # dades
        self.W_size = W_size # tamany de la finestra
        self.length = 0 # length actual de la finestra
        self.m = 0.0 # mitja de la finestra
        
    def add(self, x_t):
        """
            Afegeix una nova dada a la finestra i retorna la mitja d'aquesta i el valor de l'accio:.
            return  new_mean,-1  : si la mitja ha augmentat
					new_mean, 1  : si la mitja ha disminuït
                    new_mean, 0  : si la mitja és igual
        """
        self.data.append(x_t)
        prev_m = self.m
        self.m = np.mean(self.data[self.length-self.W_size:])
        self.length+=1
        
        chg = 0 # Si la mitja no varia, canvi (chg) val 0.
        if(self.m > prev_m): chg = -1 # si la mitja ha disminuït, canvi val -1
        elif(self.m < prev_m): chg = 1 # si la mitja ha augmentat, canvi val 1
        return self.m,chg

In [3]:
# PROVA AMB DADES SINTETIQUES

# Sliding window amb un tamany de finestra ==10
method1 = SlidWin(10)
# Sliding window amb un tamany de finestra ==50
method2 = SlidWin(50)

v=[] # Genera un mostra de 400 + 600 valors amb un canvi brusc entre 400 i 401.
for i in xrange(100):
    v.append(0.6+0.1*(random.random()-0.5))
for i in xrange(200):
    v.append(0.4+0.1*(random.random()-0.5))
   
# Anem afegint de forma seqüencial les dades dins la nostra finestra lliscant. Guardem el resultat dins la llista output1 i output2
output1=[]
output2=[]
for item in v:
    mean,action=method1.add(item)
    output1.append(mean)
    mean,action=method2.add(item)
    output2.append(mean)
    
  
# Visualitzem el resultat
pylab.plot(v)
pylab.plot(output1,'r') # plot del resultat del Mètode 1
pylab.plot(output2,'g') # plot del resultat del Mètode 2
pylab.show()



In [4]:
# Prova amb dades de la Borsa utilizant l'estratègia bàsica que us donem
method = SlidWin(10)
for item in data:
    # Creem un objecte broker. Aquest objecte cada cop que entrem una nova dada dira si hem de comprar o vendre accions
    # per_change indica el percentatge de canvi respecte al valor de l'accio per realitzar una compra/venta
    # min_time indica el temps minim que ens hem d'esperar per fer nova accio de compra/venta
    per_change=0.1
    min_time=10
    broker= StockMarketWin(method,per_change,min_time)
    # Executem l'estrtegia de comprar a partir de l'objecte broker i les dades d'entrada. La funció estrategiaBasica 
    # està definidia dins utilsP4.py. Ella és la responsable de decidir la quantatit de compra o venta d'accios a partir de la
    # suggeriencia del broker
    temp_badget,invested_money,non_strategy = estrategiaBasica(broker,data[item])
    # Mostrem els resultats per pantalla
    print_results(data[item],temp_badget,invested_money,non_strategy)


******************************************************************
*  
*  ---------Valor inicial de la acció  1.3   -------------
*  ----------------------------------------------------------
*  INICI: Diners  =  100000.0
*  INICI: Valor accions =  49999.3  ( #accions = 38461.0 *Valor Accions= 1.3 )
*  INICI: Diners sense invertir =  50000.7
*  ----------------------------------------------------------
*  ----------------------------------------------------------
*  ----------------------------------------------------------
*  FINAL: Diners  =  2090264.4
*  FINAL: Valor accions =  1560917.6  ( #accions = 43846.0 *Valor Accions= 35.6 )
*  FINAL: Diners sense invertir =  529346.8
*  ----------------------------------------------------------
*  ----------------------------------------------------------
*  Benefici Global=  1990264.4
*  Benefici produit pel Broker =  671052.8
*  ----------------------------------------------------------
******************************************************************

Exercici 2

En aquest exercici heu de definir l'algoritme de la finestra adaptativa (AdWin). Us donem l'estructura de la classe. Així com en la finestra lliscant, tenim el constructor i el mètode add. La vostra tasca consisteix en implementar el mètode add. Aquest mètode, tant a la classe anterior com en aquesta, serveix per afegir una nova dada a la finestra, en el cas de la finestra adaptativa, haurem de veure si hi ha hagut un canvi estadísticament significatiu dins les dades o no. La informació detallada de l'algortime la podeu trobar a les pàgines 30-31 de la lliço Processament de Seqüències de les classes teòriques.
Aquest mètode ha de retornar :

  • 0 si no hi ha hagut cap canvi significatiu
  • 1 si hi ha hagut un canvi i la mitja ha disminuït
  • -1 si hi ha hagut un canvi i la mitja ha augmentat

In [5]:
class AdWin:
    """
        Finestra lliscant adaptativa que permet trobar la mitja de totes les dades que es van inserin i que permet trobar canvis bruscos
        en elles i adaptar-se a ells a partir d'un valor de confiança indicat per l'usuari.
    """

    def __init__(self, d):
        """
            Constructor de la finestra.
            param d Confiança el canvi. Com més gran més fàcil serà detectar un canvi.
        """
        self.data = []
        self.length = 0 #length actual de la finestra
        self.rel = d
        self.m = 0.0
        
        
    def add(self, x_t):
        """
            Afegeix una nova dada a la finestra i després de comprovar si hi ha hagut un canvi actualitza les dades necessàries.
            param x_t Nova dada.
            return self.m Mitja de les dades del interior de la finestra
            return   0 si no hi ha hagut cap canvi significatiu
                    -1 si hi ha hagut un canvi i la mitja ha augmentat
                     1 si hi ha hagut un canvi i la mitja ha disminuït
        """
        self.data.append(x_t)
        self.length +=1
        self.m = np.mean(self.data)
        prev_m = self.m
        i = 1 
        
        while(i<self.length): #per cada una de les possibles particions de la finestra
            w0 = self.data[:i]
            w1 = self.data[i:]
            
            n0 = len(w0)  #obtenim la mida de les dues particions
            n1 = len(w1)
            m = 1./(1./n0 + 1./n1) #obtenim la mitja harmónica de les mides
            
            d_ = float(self.rel) / self.length
            e_cut = np.sqrt( (1./(2*m)) * np.log(4./d_) ) # re-calculem tall
        
            mean0 = np.mean(w0)
            mean1 = np.mean(w1)
            i+=1
            if abs(mean0 - mean1)>e_cut:
                #print '...Change Detected',e_cut
                self.data = w1[:]
                self.length = n1
                i = 1 # volem tornar a començar a generar totes les possibles particions!
            else: i+=1
            
        self.m = np.mean(self.data)
        chg = 0 # Si la mitja no varia, canvi (chg) val 0.
        if(self.m > prev_m): chg = -1 # si la mitja ha augmentat, canvi val -1
        elif(self.m < prev_m): chg = 1 # si la mitja ha disminuit, canvi val 1
        return self.m,chg

In [6]:
# PROVA AMB DADES SINTETIQUES
import time

# Adatptative window amb d=0.95
method1 = AdWin(0.95)

v=[] # Genera un mostra de 400 + 600 valors amb un canvi brusc entre 400 i 401.
'''
for i in xrange(200):
    v.append(0.6+0.1*(random.random()-0.5))
for i in xrange(400):
    v.append(0.4+0.3*(random.random()-0.5))
for i in xrange(600):
    v.append(0.1*(random.random()-0.5))
for i in xrange(400):
    v.append(0.4+0.3*(random.random()-0.5))
for i in xrange(200):
    v.append(0.6+0.1*(random.random()-0.5))
'''
for i in xrange(200):
    v.append(0.6+0.1*(random.random()-0.5))
for i in xrange(400):
    v.append(0.4+0.1*(random.random()-0.5))
for i in xrange(600):
    v.append(0.1*(random.random()-0.5))
    
# Anem afegint de forma seqüencial les dades dins la nostra finestra adaptativa. Guardem el resultat dins la llista output1
output1=[]
t1 = time.clock()
for item in v:
    mean,action=method1.add(item)
    output1.append(mean)
t2 = time.clock()
print 'Elapsed time',(t2-t1),'s' 
    
# Visualitzem el resultat
pylab.plot(v)
pylab.plot(output1,'r') # plot del resultat del Mètode 1
pylab.show()


Elapsed time 12.8 s

In [7]:
# Prova amb dades de la Borsa utilizant l'estratègia bàsica que us donem
method = AdWin(0.95)
for item in data:
    # Creem un objecte broker. Aquest objecte cada cop que entrem una nova dada dira si hem de comprar o vendre accions
    # per_change indica el percentatge de canvi respecte al valor de l'accio per realitzar una compra/venta
    # min_time indica el temps minim que ens hem d'esperar per fer nova accio de compra/venta
    broker= StockMarketWin(method,0.1,10)
    # Executem l'estrtegia de comprar a partir de l'objecte broker i les dades d'entrada. La funció estrategiaBasica 
    # està definidia dins utilsP4.py. Ella és la responsable de decidir la quantatit de compra o venta d'accios a partir de la
    # suggeriencia del broker
    temp_badget,invested_money,non_strategy= estrategiaBasica(broker,data[item])
    # Mostrem els resultats per pantalla
    print_results(data[item],temp_badget,invested_money,non_strategy)


******************************************************************
*  
*  ---------Valor inicial de la acció  1.3   -------------
*  ----------------------------------------------------------
*  INICI: Diners  =  100000.0
*  INICI: Valor accions =  49999.3  ( #accions = 38461.0 *Valor Accions= 1.3 )
*  INICI: Diners sense invertir =  50000.7
*  ----------------------------------------------------------
*  ----------------------------------------------------------
*  ----------------------------------------------------------
*  FINAL: Diners  =  9146023.7
*  FINAL: Valor accions =  8959274.0  ( #accions = 251665.0 *Valor Accions= 35.6 )
*  FINAL: Diners sense invertir =  186749.7
*  ----------------------------------------------------------
*  ----------------------------------------------------------
*  Benefici Global=  9046023.7
*  Benefici produit pel Broker =  7726812.1
*  ----------------------------------------------------------
******************************************************************

Exercici 3

Us hem donat implementada una estratègia de compra/venta d'accions de borsa molt bàsica ( funció estrategiaBasica de l'arxiu utilsP4.py). L'estratègia de la funció és la següent:

  • Gastem el 25% dels diners amb noves accions quant el broker ens diu COMPRAR i venem el 25% de les accions que tenim quant el broker ens diu VENDRE

Penseu i implementeu una nova estratègia de compra/venta d'accions. Per definir la vostra estratègia us heu de limitar a utilitzar:

  • L'acció recomanada en l'estat actual.
  • L'última acció presa.
  • El valor de la variació de la mitja.
  • </ul> Compareu els resultats amb l'estratègia bàsica. Sou capaços de guanyar diners? Són importants els paràmetres del AdWin? Quin efecte produeixen?

    
    
    In [8]:
    def estrategia(broker,data,badget=100000.00):
        # Definim els parametres inicials de l'estrategia
        # Gastem el 50% del badget en accions
        n_stocks= math.floor((badget/2)/float(data[0])) #numero de acciones que podemos comprar con  el 50% de budget
        init_stocks=n_stocks  # accions inicials
        #Descomptem del badget el valor de les acciones que hem comprat
        badget = badget - n_stocks * float(data[0]) #invertimos la mitad
        
        # inicialitzem variables
        invested_money=np.zeros(len(data))
        temp_badget=np.zeros(len(data))
        non_strategy=np.zeros(len(data))
        prev_m = 0.
        impr =0.0
        money2spend = 0.0
        actions2sell = 0.0
        cont = 0 
        
        # iterem les dades de forma seqüencial simulant una partida de joc
        for current_value in data:
            # afegim una nova dada i obtenim l'accio recomanada pel broker
            action  = broker.add(float(current_value)) #mirem si ha canviat la mitja
            
            if action != None: 
                if (prev_m != 0):
                    impr = abs((action[0]/prev_m)-1) #Obtenim la variació respecte l'anterior acció
                    #print '(previous, current):', prev_m,'\t',action[0]
                    #print '(action,improvement):',action[1],impr
                else:
                    impr = 0.0
                    #print '(previous, current):', prev_m,'\t',action[0]
                    #print '(action,improvement):',action[1],impr
                    
                if action[1] == -1: #ACCIÓ COMPRA
                    """Quan el valor de les accions puja, l'acció a realitzar és comprar.
                        La nostra estratègia té diversos aspectes a tenir en compte: 
                        1).Quan la mitja del valor puja menys d'un 5% respecte a la mitjana anterior,
                        invertim aquest percentatge de la nostra cartera(disponible)
                        2). Quan la mitja puja mes d'un 5%, tenim una pujada considerable, i ens interessa tenir molts diners invertits,
                        per tant, invertim tots els diners disponibles."""
                    if impr <= 0.05:
                        money2spend = impr*badget
                        #print '\tBuy some:',money2spend
                    else:
                        money2spend = badget
                        #print '\tBuy All budget:',money2spend
                        
                    new_actions = math.floor(money2spend/float(current_value))
                    n_stocks+= new_actions
                    badget-=new_actions*float(current_value)
                
                   
                if action[1] == 1: #ACCIÓ VENTA
                    """Quan el valor de les accions baixa, l'acció a fer es vendre.
                        La nostra estratègia consisteix en vendre nomes quan hi ha una baixada superior al 5% respecte a la mitjana anterior.
                        Quan es dona el cas, venem un 20% de les accions de les que disposem."""
                    actions2sell = 0
                    if impr > 0.05: 
                        actions2sell=round(0.2*n_stocks)
                        #print '\tSell 20%:',actions2sell
                        
                    n_stocks -= actions2sell
                    badget+=actions2sell*float(current_value)
                
                #print '\n'
                prev_m = action[0]
            temp_badget[cont] = badget
            invested_money[cont] = n_stocks*float(current_value)
            non_strategy[cont] = (badget/2) + init_stocks*float(current_value)
            cont=cont+1
            
        return temp_badget,invested_money,non_strategy
    
    
    
    In [9]:
    # Prova amb dades de la Borsa utilizant l'estratègia bàsica que us donem
    method = AdWin(0.95)
    for item in data:
        # Creem un objecte broker. Aquest objecte cada cop que entrem una nova dada dira si hem de comprar o vendre accions
        # per_change indica el percentatge de canvi respecte al valor de l'accio per realitzar una compra/venta
        # min_time indica el temps minim que ens hem d'esperar per fer nova accio de compra/venta
        broker= StockMarketWin(method,0.1,10)
        
        # Executem la nostra estrategia:
        temp_badget,invested_money,non_strategy= estrategia(broker,data[item])
        # Mostrem els resultats per pantalla
        print_results(data[item],temp_badget,invested_money,non_strategy)
    
    
    
    
    ******************************************************************
    *  
    *  ---------Valor inicial de la acció  1.3   -------------
    *  ----------------------------------------------------------
    *  INICI: Diners  =  100000.0
    *  INICI: Valor accions =  49999.3  ( #accions = 38461.0 *Valor Accions= 1.3 )
    *  INICI: Diners sense invertir =  50000.7
    *  ----------------------------------------------------------
    *  ----------------------------------------------------------
    *  ----------------------------------------------------------
    *  FINAL: Diners  =  16586544.6
    *  FINAL: Valor accions =  16586538.4  ( #accions = 465914.0 *Valor Accions= 35.6 )
    *  FINAL: Diners sense invertir =  6.19999999879
    *  ----------------------------------------------------------
    *  ----------------------------------------------------------
    *  Benefici Global=  16486544.6
    *  Benefici produit pel Broker =  15167333.0
    *  ----------------------------------------------------------
    ******************************************************************
    
    
    
    In [82]: