We need to calculate a continuously rebalanced portfolio of 2 stocks. Lets call them A and B. They shall both have an equal part of the portfolio. So if I have 100$ in my po
just a mathematical improvement on maxymoo's answer:
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])
The main idea here is to work in terms of dollars instead of ratios. If you keep track of the number of shares and the relative dollar values of the ibm and ford shares, then you can express the criterion for rebalancing as
mask = (df['ratio'] >= 1+tol) | (df['ratio'] <= 1-tol)
where the ratio equals
df['ratio'] = df['ibm value'] / df['ford value']
and df['ibm value']
, and df['ford value']
represent actual dollar values.
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])
What about this:
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
You can use this code to calulate your portfolio at each point in time.
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]