1 '''
2 Created on Sep 27, 2011
3
4 @author: John Cornwell
5 @contact: JohnWCornwellV@gmail.com
6 @summary: Various simple trading strategies to generate allocations.
7 '''
8
9 ''' Python imports '''
10 import datetime as dt
11 from math import sqrt
12
13
14 ''' 3rd party imports '''
15 import numpy as np
16 import pandas as pand
17
18 ''' QSTK imports '''
19 import qstkutil.tsutil as tsu
20
21
23 """
24 @summary Simplest strategy, weights are provided through args.
25 @param dtStart: Start date for portfolio
26 @param dtEnd: End date for portfolio
27 @param dFuncArgs: Dict of function args passed to the function
28 @return DataFrame corresponding to the portfolio allocations
29 """
30 if not dFuncArgs.has_key('dmPrice'):
31 print 'Error: Strategy requires dmPrice information'
32 return
33
34 if not dFuncArgs.has_key('lfWeights'):
35 print 'Error: Strategy requires weight information'
36 return
37
38 dmPrice = dFuncArgs['dmPrice']
39 lfWeights = dFuncArgs['lfWeights']
40
41 ''' Generate two allocations, one for the start day, one for the end '''
42 naAlloc = np.array( lfWeights ).reshape(1,-1)
43
44 dfAlloc = pand.DataFrame( index=[dtStart], data=naAlloc, columns=(dmPrice.columns) )
45 dfAlloc = dfAlloc.append( pand.DataMatrix(index=[dtEnd], data=naAlloc, columns=dmPrice.columns))
46 dfAlloc['_CASH'] = 0.0
47
48 return dfAlloc
49
51 """
52 @summary Evenly distributed strategy.
53 @param dtStart: Start date for portfolio
54 @param dtEnd: End date for portfolio
55 @param dFuncArgs: Dict of function args passed to the function
56 @return DataFrame corresponding to the portfolio allocations
57 """
58 if not dFuncArgs.has_key('dmPrice'):
59 print 'Error: Strategy requires dmPrice information'
60 return
61
62 dmPrice = dFuncArgs['dmPrice']
63
64 lNumSym = len(dmPrice.columns)
65
66 ''' Generate two allocations, one for the start day, one for the end '''
67 naAlloc = (np.array( np.ones(lNumSym) ) * (1.0 / lNumSym)).reshape(1,-1)
68 dfAlloc = pand.DataMatrix( index=[dtStart], data=naAlloc, columns=(dmPrice.columns) )
69 dfAlloc = dfAlloc.append( pand.DataMatrix(index=[dtEnd], data=naAlloc, columns=dmPrice.columns))
70 dfAlloc['_CASH'] = 0.0
71
72 return dfAlloc
73
74
76 """
77 @summary Markovitz strategy, generates a curve and then chooses a point on it.
78 @param dtStart: Start date for portfolio
79 @param dtEnd: End date for portfolio
80 @param dFuncArgs: Dict of function args passed to the function
81 @return DataFrame corresponding to the portfolio allocations
82 """
83 if not dFuncArgs.has_key('dmPrice'):
84 print 'Error:', stratMark.__name__, 'requires dmPrice information'
85 return
86
87 if not dFuncArgs.has_key('sPeriod'):
88 print 'Error:', stratMark.__name__, 'requires rebalancing period'
89 return
90
91 if not dFuncArgs.has_key('lLookback'):
92 print 'Error:', stratMark.__name__, 'requires lookback'
93 return
94
95 if not dFuncArgs.has_key('sMarkPoint'):
96 print 'Error:', stratMark.__name__, 'requires markowitz point to choose'
97 return
98
99 ''' Optional variables '''
100 if not dFuncArgs.has_key('bAddAlpha'):
101 bAddAlpha = False
102 else:
103 bAddAlpha = dFuncArgs['bAddAlpha']
104
105 dmPrice = dFuncArgs['dmPrice']
106 sPeriod = dFuncArgs['sPeriod']
107 lLookback = dFuncArgs['lLookback']
108 sMarkPoint = dFuncArgs['sMarkPoint']
109
110 ''' Select rebalancing dates '''
111 drNewRange = pand.DateRange(dtStart, dtEnd, timeRule=sPeriod) + pand.DateOffset(hours=16)
112
113 dfAlloc = pand.DataMatrix()
114 ''' Go through each rebalance date and calculate an efficient frontier for each '''
115 for i, dtDate in enumerate(drNewRange):
116 dtStart = dtDate - pand.DateOffset(days=lLookback)
117
118 if( dtStart < dmPrice.index[0] ):
119 print 'Error, not enough data to rebalance'
120 continue
121
122 naRets = dmPrice.ix[ dtStart:dtDate ].values.copy()
123 tsu.returnize1(naRets)
124 tsu.fillforward(naRets)
125 tsu.fillbackward(naRets)
126
127 ''' Add alpha to returns '''
128 if bAddAlpha:
129 if i < len(drNewRange) - 1:
130 naFutureRets = dmPrice.ix[ dtDate:drNewRange[i+1] ].values.copy()
131 tsu.returnize1(naFutureRets)
132 tsu.fillforward(naFutureRets)
133 tsu.fillbackward(naFutureRets)
134
135 naAvg = np.mean( naFutureRets, axis=0 )
136
137 ''' make a mix of past/future rets '''
138 for i in range( naRets.shape[0] ):
139 naRets[i,:] = (naRets[i,:] + (naAvg*0.05)) / 1.05
140
141
142 ''' Generate the efficient frontier '''
143 (lfReturn, lfStd, lnaPortfolios) = getFrontier( naRets, fUpper=0.2, fLower=0.01 )
144
145 lInd = 0
146
147 '''
148 plt.clf()
149 plt.plot( lfStd, lfReturn)'''
150
151 if( sMarkPoint == 'Sharpe'):
152 ''' Find portfolio with max sharpe '''
153 fMax = -1E300
154 for i in range( len(lfReturn) ):
155 fShrp = (lfReturn[i]-1) / (lfStd[i])
156 if fShrp > fMax:
157 fMax = fShrp
158 lInd = i
159 '''
160 plt.plot( [lfStd[lInd]], [lfReturn[lInd]], 'ro')
161 plt.draw()
162 time.sleep(2)
163 plt.show()'''
164
165 elif( sMarkPoint == 'MinVar'):
166 ''' use portfolio with minimum variance '''
167 fMin = 1E300
168 for i in range( len(lfReturn) ):
169 if lfStd[i] < fMin:
170 fMin = lfStd[i]
171 lInd = i
172
173 elif( sMarkPoint == 'MaxRet'):
174 ''' use Portfolio with max returns (not really markovitz) '''
175 lInd = len(lfReturn)-1
176
177 elif( sMarkPoint == 'MinRet'):
178 ''' use Portfolio with min returns (not really markovitz) '''
179 lInd = 0
180
181 else:
182 print 'Warning: invalid sMarkPoint'''
183 return
184
185
186
187 ''' Generate allocation based on selected portfolio '''
188 naAlloc = (np.array( lnaPortfolios[lInd] ).reshape(1,-1) )
189 dmNew = pand.DataMatrix( index=[dtDate], data=naAlloc, columns=(dmPrice.columns) )
190 dfAlloc = dfAlloc.append( dmNew )
191
192 dfAlloc['_CASH'] = 0.0
193 return dfAlloc
194
196 """
197 @summary Calls stratMark with sharpe ratio point.
198 @param dtStart: Start date for portfolio
199 @param dtEnd: End date for portfolio
200 @param dFuncArgs: Dict of function args passed to the function
201 @return DataFrame corresponding to the portfolio allocations
202 """
203 dFuncArgs['sMarkPoint'] = 'Sharpe'
204 return stratMark( dtStart, dtEnd, dFuncArgs )
205
207 """
208 @summary Calls stratMark and uses lowest variance ratio point.
209 @param dtStart: Start date for portfolio
210 @param dtEnd: End date for portfolio
211 @param dFuncArgs: Dict of function args passed to the function
212 @return DataFrame corresponding to the portfolio allocations
213 """
214 dFuncArgs['sMarkPoint'] = 'MinVar'
215 return stratMark( dtStart, dtEnd, dFuncArgs )
216
218 """
219 @summary Calls stratMark and uses maximum returns.
220 @param dtStart: Start date for portfolio
221 @param dtEnd: End date for portfolio
222 @param dFuncArgs: Dict of function args passed to the function
223 @return DataFrame corresponding to the portfolio allocations
224 """
225 dFuncArgs['sMarkPoint'] = 'MaxRet'
226 return stratMark( dtStart, dtEnd, dFuncArgs )
227
229 """
230 @summary Calls stratMark and uses minimum returns.
231 @param dtStart: Start date for portfolio
232 @param dtEnd: End date for portfolio
233 @param dFuncArgs: Dict of function args passed to the function
234 @return DataFrame corresponding to the portfolio allocations
235 """
236 dFuncArgs['sMarkPoint'] = 'MinRet'
237 return stratMark( dtStart, dtEnd, dFuncArgs )
238
240 """
241 @summary Calls stratMark and chooses the highest share point, uses future knowlege (alpha).
242 @param dtStart: Start date for portfolio
243 @param dtEnd: End date for portfolio
244 @param dFuncArgs: Dict of function args passed to the function
245 @return DataFrame corresponding to the portfolio allocations
246 """
247 dFuncArgs['sMarkPoint'] = 'Sharpe'
248 dFuncArgs['bAddAlpha'] = True
249 return stratMark( dtStart, dtEnd, dFuncArgs )
250
252 """
253 @summary Calls stratMark chooses the highest returns point, uses future knowlege (alpha).
254 @param dtStart: Start date for portfolio
255 @param dtEnd: End date for portfolio
256 @param dFuncArgs: Dict of function args passed to the function
257 @return DataFrame corresponding to the portfolio allocations
258 """
259 dFuncArgs['sMarkPoint'] = 'MaxRet'
260 dFuncArgs['bAddAlpha'] = True
261 return stratMark( dtStart, dtEnd, dFuncArgs )
262