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>
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)
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)
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 :
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()
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)
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:
Penseu i implementeu una nova estratègia de compra/venta d'accions. Per definir la vostra estratègia us heu de limitar a utilitzar:
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)
In [82]: