I am trying to construct trend following momentum portfolio strategy based on S&P500 index (momthly data)
I used Kaufmann’s fractal efficiency ratio to filter out whipsaw signal (http://etfhq.com/blog/2011/02/07/kaufmans-efficiency-ratio/)
I succeeded in coding, but it’s very clumsy, so I need advice for better code.
Strategy
- Get data of S&P 500 index from yahoo finance
- Calculate Kaufmann’s efficiency ratio on lookback period X (1 , if close > close(n), 0)
- Averages calculated value of 2, from 1 to 12 time period —> Monthly asset allocation ratio, 1-asset allocation ratio = cash (3% per year)
I am having a difficulty in averaging 1 to 12 efficiency ratio. Of course I know that it can be simply implemented by for loop and it’s very easy task, but I failed.
I need more concise and refined code, anybody can help me?
a['meanfractal']
bothers me in the code below..
import pandas as pd import matplotlib.pyplot as plt import numpy as np import pandas_datareader.data as web def price(stock, start): price = web.DataReader(name=stock, data_source='yahoo', start=start)['Adj Close'] return price.div(price.iat[0]).resample('M').last().to_frame('price') a = price('SPY','2000-01-01') def fractal(a,p): a['direction'] = np.where(a['price'].diff(p)>0,1,0) a['abs'] = a['price'].diff(p).abs() a['volatility'] = a.price.diff().abs().rolling(p).sum() a['fractal'] = a['abs'].values/a['volatility'].values*a['direction'].values return a['fractal'] def meanfractal(a): a['meanfractal']= (fractal(a,1).values+fractal(a,2).values+fractal(a,3).values+fractal(a,4).values+fractal(a,5).values+fractal(a,6).values+fractal(a,7).values+fractal(a,8).values+fractal(a,9).values+fractal(a,10).values+fractal(a,11).values+fractal(a,12).values)/12 a['portfolio1'] = (a.price/a.price.shift(1).values*a.meanfractal.shift(1).values+(1-a.meanfractal.shift(1).values)*1.03**(1/12)).cumprod() a['portfolio2'] = ((a.price/a.price.shift(1).values*a.meanfractal.shift(1).values+1.03**(1/12))/(1+a.meanfractal.shift(1))).cumprod() a=a.dropna() a=a.div(a.ix[0]) return a[['price','portfolio1','portfolio2']].plot() print(a) plt.show()
Advertisement
Answer
You could simplify further by storing the values corresponding to p
in a DF
rather than computing for each series separately as shown:
def fractal(a, p): df = pd.DataFrame() for count in range(1,p+1): a['direction'] = np.where(a['price'].diff(count)>0,1,0) a['abs'] = a['price'].diff(count).abs() a['volatility'] = a.price.diff().abs().rolling(count).sum() a['fractal'] = a['abs']/a['volatility']*a['direction'] df = pd.concat([df, a['fractal']], axis=1) return df
Then, you could assign the repeating operations to a variable which reduces the re-computation time.
def meanfractal(a, l=12): a['meanfractal']= pd.DataFrame(fractal(a, l)).sum(1,skipna=False)/l mean_shift = a['meanfractal'].shift(1) price_shift = a['price'].shift(1) factor = 1.03**(1/l) a['portfolio1'] = (a['price']/price_shift*mean_shift+(1-mean_shift)*factor).cumprod() a['portfolio2'] = ((a['price']/price_shift*mean_shift+factor)/(1+mean_shift)).cumprod() a.dropna(inplace=True) a = a.div(a.ix[0]) return a[['price','portfolio1','portfolio2']].plot()
Resulting plot obtained:
meanfractal(a)
Note: If speed is not a major concern, you could perform the operations via the built-in methods present in pandas
instead of converting them into it’s corresponding numpy
array values.