2015-06-10 13 views
9

Dobbiamo calcolare un portafoglio costantemente ribilanciato di 2 titoli. Li chiamiamo A e B. Entrambi avranno una parte uguale del portafoglio. Quindi se avessi 100 $ nel mio portafoglio 50 $ investiremo in A e 50 $ in B. Dato che entrambe le azioni hanno performance molto diverse, non manterranno i loro pesi uguali (dopo 3 mesi già A potrebbe valere 70 $ mentre B sceso a 45 $). Il problema è che devono mantenere la loro quota del portafoglio entro una certa larghezza di banda di tolleranza. Questa larghezza di banda è del 5%. Quindi ho bisogno di una funzione che: Se A> B * 1.05 o A * 1.05 < B, riequilibrare.Riequilibrio del portafoglio con il metodo della larghezza di banda in python

Questa prima parte serve solo per ottenere il modo più veloce di alcuni dati per avere una base comune di discussione e per rendere i risultati comparabili, così appena è possibile copiare e incollare tutto questo codice e funziona per voi ..

import pandas as pd 
from datetime import datetime 
import numpy as np 


df1 = pd.io.data.get_data_yahoo("IBM", 
           start=datetime(1970, 1, 1), 
           end=datetime.today()) 
df1.rename(columns={'Adj Close': 'ibm'}, inplace=True) 

df2 = pd.io.data.get_data_yahoo("F", 
           start=datetime(1970, 1, 1), 
           end=datetime.today()) 
df2.rename(columns={'Adj Close': 'ford'}, inplace=True) 

df = df1.join(df2.ford, how='inner') 
del df["Open"] 
del df["High"] 
del df["Low"] 
del df["Close"] 
del df["Volume"] 

Ora inizia a calcolare la performance relativa di ogni stock con la formula: df.ibm/df.ibm[0]. Il problema è che non appena interrompiamo la prima larghezza di banda, dobbiamo reimpostare lo 0 nella nostra formula: df.ibm/df.ibm[0], dal momento che ribilanciamo e dobbiamo iniziare a calcolare da quel momento in poi. Quindi usiamo df.d per questa funzione segnaposto e lo impostiamo uguale a df.t non appena una larghezza di banda viene interrotta df.t fondamentalmente conta solo la lunghezza del dataframe e può dirci quindi sempre "dove siamo". Quindi, ecco il calcolo effettivo inizio:

tol = 0.05 #settintg the bandwidth tolerance 
df["d"]= 0 # 
df["t"]= np.arange(len(df)) 
tol = 0.3 

def flex_relative(x): 
    if df.ibm/df.ibm.iloc[df.d].values < df.ford/df.ford.iloc[df.d].values * (1+tol): 
     return df.iloc[df.index.get_loc(x.name) - 1]['d'] == df.t 
    elif df.ibm/df.ibm.iloc[df.d].values > df.ford/df.ford.iloc[df.d].values * (1+tol): 
     return df.iloc[df.index.get_loc(x.name) - 1]['d'] == df.t 
    else: 
     return df.ibm/df.ibm.iloc[df.d].values, df.ford/df.ford.iloc[df.d].values 



df["ibm_performance"], df["ford_performance"], = df.apply(flex_relative, axis =1) 

Il problema è che sto ottenendo questo errore formare l'ultima riga di codice, dove cerco di applicare la funzione con df.apply(flex_relative, axis =1)

ValueError: ('The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().', u'occurred at index 1972-06-01 00:00:00') Il problema è che nessuno delle opzioni date della dichiarazione di errore risolve il mio problema, quindi davvero non so cosa fare ...

L'unica cosa che ho trovato finora è stato il collegamento sottostante, ma chiamare una funzione R non funzionerà per me perché ho bisogno di applicarlo a dataset piuttosto grandi e posso anche implementare un'ottimizzazione in questa funzione, s o ha sicuramente bisogno di essere costruito in python. Ecco il link in ogni caso: Finance Lib with portfolio optimization method in python

manualmente (ciò che non è un buon modo per gestire dati di grandi dimensioni), ho calcolato che la prima data per un riequilibrio potrebbe essere: 03.11.1972 00:00:00

L'uscita del dataframe alla prima il ribilanciamento dovrebbe assomigliare a questo:

     ibm  ford  d t ibm_performance ford_performance 
1972-11-01 00:00:00 6,505655 0,387415 0 107 1,021009107 0,959552418 
1972-11-02 00:00:00 6,530709 0,398136 0 108 1,017092172 0,933713605 
1972-11-03 00:00:00 6,478513 0,411718 0 109 1,025286667 0,902911702 # this is the day, the rebalancing was detected 
1972-11-06 00:00:00 6,363683 0,416007 109 110 1,043787536 0,893602752 # this is the day the day the rebalancing is implemented, therefore df.d gets set = df.t = 109 
1972-11-08 00:00:00 6,310883 0,413861 109 111 1,052520384 0,898236364 
1972-11-09 00:00:00 6,227073 0,422439 109 112 1,066686226 0,879996875 

Grazie mille per il vostro supporto!

@Alexander: Sì, il ribilanciamento avverrà il giorno successivo.

@maxymoo: se si implementa questo codice dopo il proprio, si ottiene il peso del portafoglio di ogni stock e non si fermano tra il 45 e il 55%. È piuttosto tra il 75% e il 25%:

df["ford_weight"] = df.ford_prop*df.ford/(df.ford_prop*df.ford+df.ibm_prop*df.ibm) #calculating the actual portfolio weights 
df["ibm_weight"] = df.ibm_prop*df.ibm/(df.ford_prop*df.ford+df.ibm_prop*df.ibm) 

print df 
print df.ibm_weight.min() 
print df.ibm_weight.max() 
print df.ford_weight.min() 
print df.ford_weight.max() 

ho provato non per un'ora o così da risolvere, ma non lo ha trovato.

Posso fare qualcosa per chiarire questa domanda?

+2

Dato che si sta utilizzando solo il ciclo prossima, posso presumo che la regola è quella di riequilibrare il giorno successivo se i vostri pesi sono al di fuori della tolleranza (O semplicemente per scontato si può riequilibrare a distanza, se il prezzo di chiusura adj potrebbe far sì che i tuoi pesi siano al di fuori della tolleranza?). – Alexander

risposta

6

L'idea principale è quella di lavorare in termini di dollari invece di rapporti. Se tenere traccia del numero di azioni ed i valori in dollari relativi di IBM e Ford azioni, allora si può esprimere il criterio di riequilibrio come

mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol) 

dove il rapporto è pari a

df['ratio'] = df['ibm value']/df['ford value'] 

e df['ibm value'] e df['ford value'] rappresentano valori effettivi in ​​dollari.


import datetime as DT 
import numpy as np 
import pandas as pd 
import pandas.io.data as PID 

def setup_df(): 
    df1 = PID.get_data_yahoo("IBM", 
          start=DT.datetime(1970, 1, 1), 
          end=DT.datetime.today()) 
    df1.rename(columns={'Adj Close': 'ibm'}, inplace=True) 

    df2 = PID.get_data_yahoo("F", 
          start=DT.datetime(1970, 1, 1), 
          end=DT.datetime.today()) 
    df2.rename(columns={'Adj Close': 'ford'}, inplace=True) 

    df = df1.join(df2.ford, how='inner') 
    df = df[['ibm', 'ford']] 
    df['sh ibm'] = 0 
    df['sh ford'] = 0 
    df['ibm value'] = 0 
    df['ford value'] = 0 
    df['ratio'] = 0 
    return df 

def invest(df, i, amount): 
    """ 
    Invest amount dollars evenly between ibm and ford 
    starting at ordinal index i. 
    This modifies df. 
    """ 
    c = dict([(col, j) for j, col in enumerate(df.columns)]) 
    halfvalue = amount/2 
    df.iloc[i:, c['sh ibm']] = halfvalue/df.iloc[i, c['ibm']] 
    df.iloc[i:, c['sh ford']] = halfvalue/df.iloc[i, c['ford']] 

    df.iloc[i:, c['ibm value']] = (
     df.iloc[i:, c['ibm']] * df.iloc[i:, c['sh ibm']]) 
    df.iloc[i:, c['ford value']] = (
     df.iloc[i:, c['ford']] * df.iloc[i:, c['sh ford']]) 
    df.iloc[i:, c['ratio']] = (
     df.iloc[i:, c['ibm value']]/df.iloc[i:, c['ford value']]) 

def rebalance(df, tol, i=0): 
    """ 
    Rebalance df whenever the ratio falls outside the tolerance range. 
    This modifies df. 
    """ 
    c = dict([(col, j) for j, col in enumerate(df.columns)]) 
    while True: 
     mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol) 
     # ignore prior locations where the ratio falls outside tol range 
     mask[:i] = False 
     try: 
      # Move i one index past the first index where mask is True 
      # Note that this means the ratio at i will remain outside tol range 
      i = np.where(mask)[0][0] + 1 
     except IndexError: 
      break 
     amount = (df.iloc[i, c['ibm value']] + df.iloc[i, c['ford value']]) 
     invest(df, i, amount) 
    return df 

df = setup_df() 
tol = 0.05 
invest(df, i=0, amount=100) 
rebalance(df, tol) 

df['portfolio value'] = df['ibm value'] + df['ford value'] 
df['ibm weight'] = df['ibm value']/df['portfolio value'] 
df['ford weight'] = df['ford value']/df['portfolio value'] 

print df['ibm weight'].min() 
print df['ibm weight'].max() 
print df['ford weight'].min() 
print df['ford weight'].max() 

# This shows the rows which trigger rebalancing 
mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol) 
print(df.loc[mask]) 
+1

quindi per capire che 100% - df.ratio è impostato a 1 se si verifica un ribilanciamento, giusto? –

+2

e se voglio impostare altri pesi oltre il 50/50, aggiusto semplicemente la variabile "mezzo valore"? –

+1

@ hb.klein: corretto su entrambi i punti. – unutbu

3

È possibile utilizzare questo codice per calulare il portafoglio in ogni momento.

i = df.index[0] 
df['ibm_prop'] = 0.5/df.ibm.ix[i] 
df['ford_prop'] = 0.5/df.ford.ix[i] 

while i: 
    try: 
     i = df[abs(1-(df.ibm_prop*df.ibm + df.ford_prop*df.ford)) > tol].index[0] 
    except IndexError: 
     break 
    df['ibm_prop'].ix[i:] = 0.5/df.ibm.ix[i] 
    df['ford_prop'].ix[i:] = 0.5/df.ford.ix[i] 
2

solo un miglioramento matematica sulla risposta maxymoo:

i = df.index[0] 
df['ibm_prop'] = df.ibm.ix[i]/(df.ibm.ix[i]+df.ford.ix[i]) 
df['ford_prop'] = df.ford.ix[i]/(df.ibm.ix[i]+df.ford.ix[i]) 

while i: 
    try: 
     i = df[abs((df.ibm_prop*df.ibm - df.ford_prop*df.ford)) > tol].index[0] 
    except IndexError: 
     break 
    df['ibm_prop'].ix[i:] = df.ibm.ix[i]/(df.ibm.ix[i]+df.ford.ix[i]) 
    df['ford_prop'].ix[i:] = df.ford.ix[i]/(df.ibm.ix[i]+df.ford.ix[i]) 
2

Che dire di questo:

df["d"]= [0,0,0,0,0,0,0,0,0,0] 
df["t"]= np.arange(len(df)) 
tol = 0.05 

def flex_relative(x): 
    if df.ibm/df.ibm.iloc[df.d].values < df.ford/df.ford.iloc[df.d].values * (1+tol): 
     return df.iloc[df.index.get_loc(x.name) - 1]['d'] == df.t 
    elif df.ibm/df.ibm.iloc[df.d].values > df.ford/df.ford.iloc[df.d].values * (1+tol): 
     return df.iloc[df.index.get_loc(x.name) - 1]['d'] == df.t 
+1

Aumenta lo stesso errore di valore, ma grazie. 'ValueError: ('Il valore di verità di una serie è ambiguo. Usa a.empty, a.bool(), a.item(), a.any() o a.all().', Hai fatto un errore nell'indice 1972-06-01 00:00:00 ') ' –

Problemi correlati