├── .project
├── .project~
├── .pydevproject
├── .pydevproject~
├── LICENSE
├── README.md
├── __init__.py
├── assetprices.csv
├── carry.py
├── common.py
├── commonrandom.py
├── config.csv
├── data
├── AEX_carrydata.csv
├── AEX_price.csv
├── AUD_carrydata.csv
├── AUD_price.csv
├── BOBL_carrydata.csv
├── BOBL_price.csv
├── BTP_carrydata.csv
├── BTP_price.csv
├── BUND_carrydata.csv
├── BUND_price.csv
├── CAC_carrydata.csv
├── CAC_price.csv
├── COPPER_carrydata.csv
├── COPPER_price.csv
├── CORN_carrydata.csv
├── CORN_price.csv
├── CRUDE_W_carrydata.csv
├── CRUDE_W_price.csv
├── EDOLLAR_carrydata.csv
├── EDOLLAR_price.csv
├── EUROSTX_carrydata.csv
├── EUROSTX_price.csv
├── EUR_carrydata.csv
├── EUR_price.csv
├── GAS_US_carrydata.csv
├── GAS_US_price.csv
├── GBP_carrydata.csv
├── GBP_price.csv
├── GOLD_carrydata.csv
├── GOLD_price.csv
├── JPY_carrydata.csv
├── JPY_price.csv
├── KOSPI_carrydata.csv
├── KOSPI_price.csv
├── KR10_carrydata.csv
├── KR10_price.csv
├── KR3_carrydata.csv
├── KR3_price.csv
├── LEANHOG_carrydata.csv
├── LEANHOG_price.csv
├── LIVECOW_carrydata.csv
├── LIVECOW_price.csv
├── MXP_carrydata.csv
├── MXP_price.csv
├── NASDAQ_carrydata.csv
├── NASDAQ_price.csv
├── NZD_carrydata.csv
├── NZD_price.csv
├── OAT_carrydata.csv
├── OAT_price.csv
├── PALLAD_carrydata.csv
├── PALLAD_price.csv
├── PLAT_carrydata.csv
├── PLAT_price.csv
├── SHATZ_carrydata.csv
├── SHATZ_price.csv
├── SMI_carrydata.csv
├── SMI_price.csv
├── SOYBEAN_carrydata.csv
├── SOYBEAN_price.csv
├── SP500_carrydata.csv
├── SP500_price.csv
├── US10_carrydata.csv
├── US10_price.csv
├── US20_carrydata.csv
├── US20_price.csv
├── US2_carrydata.csv
├── US2_price.csv
├── US5_carrydata.csv
├── US5_price.csv
├── V2X_carrydata.csv
├── V2X_price.csv
├── VIX_carrydata.csv
├── VIX_price.csv
├── WHEAT_carrydata.csv
└── WHEAT_price.csv
├── ewmac.py
├── fed_10yearinflation_US.csv
├── fed_aaa_US.csv
├── fed_baa_US.csv
├── google-chrome-stable_current_i386.deb
├── handcraftweights.csv
├── investigateforecasts.py
├── iteratedoptimisation.py
├── oecd_rates.csv
├── oecd_short_rates.csv
├── optimisation.py
├── plots_for_perhaps
├── MSCIWorldweights.csv
├── MSCIWorldweights.ods
├── MSCIWorldweights_US_Nonus.csv
├── MSCI_data.csv
├── MSCI_ref.csv
├── USmonthlyreturns.csv
├── USmonthlyreturns.ods
├── USrealreturns.csv
├── USrealreturnsADJ.csv
├── Untitled 2.ods
├── XIU_holdings.csv
├── XIU_holdings.ods
├── XIU_holdings1.csv
├── XIU_holdings2.csv
├── XIU_holdings3.csv
├── allcapweights.csv
├── allhcweights.csv
├── asiapacopt.py
├── assetallocationmodel.py
├── basicopt.py
├── birdinthehand.py
├── compareoptmethods.py
├── conditionalforecast.py
├── correlatedreturns.py
├── costbreakevenplots.py
├── costplots.py
├── costs.py
├── devcapweights.csv
├── devhcweights.csv
├── divbenefits.py
├── diversification_stacked.py
├── diversification_with_costs.py
├── diversificationbenefits.py
├── equalweighting.py
├── equitycountrymodels.py
├── equitysectorweights.py
├── etfvssharesbreakeven.csv
├── etfvssharesbreakevenwithtrading.csv
├── fanchart.py
├── hist_Estimate_new.py
├── hist_estimates.py
├── histofinvestmentgame.py
├── msciworld.py
├── optimisingequities.py
├── optimumrebalance3assets.py
├── plotcontango.py
├── realreturns.py
├── regionalcorrplots.py
├── relative_perf_uncertainty.py
├── sectorreturns .csv
├── sectorreturns.csv
├── showrubbishopt.py
├── smartbeta.py
├── stockselection.py
├── whichriskappetite.py
└── yieldsprediction.py
├── randomadvanced.py
├── randomanalysisofequitycurveproperties.py
├── randomexplorationofportfolioallocation.py
├── randomportfolioexample.py
├── randompriceexample.py
├── randomskewreturnsexample.py
├── randomtestequitycurvetrading.py
├── tradingrules.py
└── whichcurvewouldyoupick.py
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Systematic Trading Examples
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.project~:
--------------------------------------------------------------------------------
1 |
2 |
3 | UK CGT calculator my version
4 |
5 |
6 |
7 |
8 |
9 | org.python.pydev.PyDevBuilder
10 |
11 |
12 |
13 |
14 |
15 | org.python.pydev.pythonNature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.pydevproject:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /systematictradingexamples/
5 | /systematictradingexamples/python_code
6 |
7 | python 2.7
8 | Default
9 |
10 |
--------------------------------------------------------------------------------
/.pydevproject~:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /mytax/
5 | /mytax/python_code
6 |
7 | python 2.7
8 | Default
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # systematictradingexamples
2 | Examples of code related to book https://www.systematicmoney.org/systematic-trading and blog https://qoppac.blogspot.com
3 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robcarver17/systematictradingexamples/34b3a457811fa2d7be1955028f10fdb823c656a1/__init__.py
--------------------------------------------------------------------------------
/carry.py:
--------------------------------------------------------------------------------
1 | """
2 | This file shows how to calculate the Carry trading rule for crude oil futures
3 |
4 | As in chapter 7 / appendix B of "Systematic Trading" by Robert Carver (www.systematictrading.org)
5 |
6 | Required: pandas, matplotlib
7 |
8 | USE AT YOUR OWN RISK! No warranty is provided or implied.
9 |
10 | Handling of NAN's and Inf's isn't done here (except within pandas),
11 | And there is no error handling!
12 |
13 | """
14 |
15 | import pandas as pd
16 | import numpy as np
17 | import matplotlib.pyplot as plt
18 | from common import cap_series, pd_readcsv, find_datediff, get_price_for_instrument, get_carry_data, ROOT_DAYS_IN_YEAR
19 |
20 |
21 | ## This is the stitched price series
22 | ## We can't use the price of the contract we're trading, or the volatility will be jumpy
23 | code="US10"
24 | price=get_price_for_instrument(code)
25 |
26 |
27 | """
28 | Formulation here will work whether we are trading the nearest contract or not
29 |
30 | For other asset classes you will have to work out nerpu (net expected return in price units) yourself
31 | """
32 |
33 | data=get_carry_data(code)
34 | data[['TRADED','NEARER']].plot()
35 | plt.show()
36 |
37 |
38 |
39 | #d1=pd.datetime(2007,1,1)
40 | #d2=pd.datetime(2009,12,31)
41 |
42 |
43 | nerpu=data.apply(find_datediff, axis=1)
44 | nerpu.plot()
45 | plt.title("Nerpu")
46 | plt.show()
47 |
48 | ## Shouldn't need changing
49 | vol_lookback=25
50 | stdev_returns=pd.ewmstd(price - price.shift(1), span=vol_lookback)
51 | ann_stdev=stdev_returns*ROOT_DAYS_IN_YEAR
52 |
53 | raw_carry=(nerpu/ann_stdev.transpose()).transpose()
54 | f_scalar=30.0
55 |
56 | raw_carry.plot()
57 | plt.title("Raw carry")
58 | plt.show()
59 |
60 | forecast=raw_carry*f_scalar
61 |
62 | c_forecast=cap_series(forecast).to_frame()
63 |
64 | data_to_plot=pd.concat([forecast,c_forecast], axis=1)
65 | data_to_plot.columns=['Forecast','Capped forecast']
66 | data_to_plot.plot()
67 | plt.show()
68 |
69 |
--------------------------------------------------------------------------------
/common.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | import scipy.stats as st
4 | DAYS_IN_YEAR=256.0
5 | ROOT_DAYS_IN_YEAR=DAYS_IN_YEAR**.5
6 |
7 | useroot=""
8 |
9 | def cap_forecast(xrow, capmin,capmax):
10 | """
11 | Cap forecasts.
12 |
13 | """
14 |
15 | ## Assumes we have a single column
16 | x=xrow[0]
17 |
18 | if xcapmax:
21 | return capmax
22 |
23 | return x
24 |
25 | def cap_series(xseries, capmin=-20.0,capmax=20.0):
26 | """
27 | Apply capping to each element of a time series
28 | For a long only investor, replace -20.0 with 0.0
29 |
30 | """
31 | return xseries.apply(cap_forecast, axis=1, args=(capmin, capmax))
32 |
33 | def get_list_code():
34 | ans=pd.read_csv("%sconfig.csv" % useroot)
35 | return list(ans.Instrument)
36 |
37 | def get_point_sizes():
38 | ans=pd.read_csv("%sconfig.csv" % useroot)
39 | psizes=dict([(x[1].Instrument, float(x[1].Pointsize)) for x in ans.iterrows()])
40 | return psizes
41 |
42 |
43 | def pd_readcsv(filename):
44 | """
45 | Reads the pandas dataframe from a filename, given the index is correctly labelled
46 | """
47 |
48 |
49 | ans=pd.read_csv(filename)
50 |
51 | ans.index=pd.to_datetime(ans['DATETIME'])
52 | del ans['DATETIME']
53 | ans.index.name=None
54 | return ans
55 |
56 | def find_datediff(data_row):
57 | """
58 | data differential for a single row
59 | """
60 | if np.isnan(data_row.NEAR_MONTH) or np.isnan(data_row.TRADE_MONTH):
61 | return np.nan
62 | nearest_dt=pd.to_datetime(str(int(data_row.NEAR_MONTH)), format="%Y%m")
63 | trade_dt=pd.to_datetime(str(int(data_row.TRADE_MONTH)), format="%Y%m")
64 |
65 | distance = trade_dt - nearest_dt
66 | distance_years=distance.days/365.25
67 |
68 | ## if nearder contract is cheaper; price will fall
69 | price_diff=data_row.NEARER - data_row.TRADED
70 |
71 | return price_diff/distance_years
72 |
73 |
74 |
75 | def ewmac_forecast_scalar(Lfast, Lslow):
76 | """
77 | Function to return the forecast scalar (table 49 of the book)
78 |
79 | Only defined for certain values
80 | """
81 |
82 |
83 | fsdict=dict(l2_8=10.6, l4_16=7.5, l8_32=5.3, l16_64=3.75, l32_128=2.65, l64_256=1.87)
84 |
85 | lkey="l%d_%d" % (Lfast, Lslow)
86 |
87 | if lkey in fsdict:
88 | return fsdict[lkey]
89 | else:
90 | print "Warning: No scalar defined for Lfast=%d, Lslow=%d, using default of 1.0" % (Lfast, Lslow)
91 | return 1.0
92 |
93 | def get_price_for_instrument(code):
94 |
95 | filename="%sdata/%s_price.csv" % (useroot, code)
96 | price=pd_readcsv(filename)
97 | return price
98 |
99 | def get_carry_data(code):
100 |
101 | filename="%sdata/%s_carrydata.csv" % (useroot, code)
102 | data=pd_readcsv(filename)
103 |
104 | return data
105 |
106 | def uniquets(df3):
107 | """
108 | Makes x unique
109 | """
110 | df3=df3.groupby(level=0).first()
111 | return df3
112 |
113 |
114 | def daily_resample(b, a):
115 | """
116 | Returns b dataframe resampled to a dataframe index
117 |
118 | """
119 |
120 | master_index=a.index
121 | a_daily=a.resample('1D') ## Only want index, fill method is irrelevant
122 | b=uniquets(b)
123 | b_daily=b.reindex(a_daily.index, method="ffill", limit=1)
124 | new_b=b_daily.reindex(master_index, method="ffill", limit=1)
125 |
126 | return new_b
127 |
128 |
129 |
130 | def calculate_pandl(position_ts, price_ts, pointsize=1.0):
131 | rs_positions_ts=daily_resample(position_ts, price_ts).ffill()
132 | rets=price_ts - price_ts.shift(1)
133 | local_rets=rs_positions_ts.shift(1)*rets*pointsize
134 |
135 | return local_rets
136 |
137 | def annualised_rets(total_rets):
138 | mean_rets=total_rets.mean(skipna=True)
139 | annualised_rets=mean_rets*DAYS_IN_YEAR
140 | return annualised_rets
141 |
142 | def annualised_vol(total_rets):
143 | actual_total_daily_vol=total_rets.std(skipna=True)
144 | actual_total_annual_vol=actual_total_daily_vol*ROOT_DAYS_IN_YEAR
145 | return actual_total_annual_vol
146 |
147 | def sharpe(total_rets):
148 |
149 | sharpe=annualised_rets(total_rets)/annualised_vol(total_rets)
150 |
151 | return sharpe
152 |
153 | def stack_ts(tslist, start_date=pd.datetime(1970,1,1)):
154 |
155 | """
156 | Take a list of time series, and stack them, generating a new time series
157 | """
158 |
159 |
160 | tslist_values=[list(x.iloc[:,0].values) for x in tslist]
161 | stack_values=sum(tslist_values, [])
162 | stack_values=[x for x in stack_values if not np.isinf(x)]
163 |
164 | stacked=arbitrary_timeindex(stack_values, start_date)
165 |
166 |
167 | return stacked
168 |
169 | def slices_for_ts(data, freq="12M"):
170 | """
171 | Return date indices for slicing up a data frame
172 | """
173 | yridx=list(pd.date_range(start=data.index[0], end=data.index[-1], freq=freq))
174 | yridx_stub=list(pd.date_range(start=yridx[-1], periods=2, freq=freq))[-1]
175 | yridx=yridx+[yridx_stub]
176 |
177 | return yridx
178 |
179 | def break_up_ts(data, freq="12M"):
180 | """
181 | Take a data frame and break it into chunks
182 | returns a list of data frames
183 | """
184 | yridx=slices_for_ts(data, freq)
185 |
186 | brokenup=[]
187 | for idx in range(len(yridx))[1:]:
188 | brokenup.append(data[yridx[idx-1]:yridx[idx]])
189 |
190 | return brokenup
191 |
192 |
193 | def drawdown(x):
194 | ### Returns a ts of drawdowns for a time series x
195 |
196 | ## rolling max with infinite window
197 | maxx=pd.rolling_max(x, 99999999, min_periods=1)
198 | return (x - maxx)/maxx
199 |
200 |
201 |
202 |
203 | class account_curve(pd.core.series.Series):
204 | """
205 | Inherits from pandas time series to give useful information
206 |
207 | Could be in % or GBP terms
208 |
209 | Downsamples to daily before doing anything else
210 |
211 | Can
212 |
213 | """
214 |
215 |
216 | def new_freq(self, freq):
217 | ## Set up a new frequency.
218 | ## Note this will break certain things (eg Sharpe) so be careful
219 | if freq=="Daily":
220 | ## we assume we're daily so do nothing
221 | return self
222 | if freq=="Weekly":
223 | return self.cumsum().ffill().resample("W").diff()
224 | if freq=="Monthly":
225 | return self.cumsum().ffill().resample("M").diff()
226 |
227 | def sharpe(self):
228 | ## assumes daily returns
229 | return ROOT_DAYS_IN_YEAR*self.mean()/self.std()
230 |
231 | def annstd(self):
232 | return ROOT_DAYS_IN_YEAR*self.std()
233 |
234 | def losses(self):
235 | x=self.values
236 | return [z for z in x if z<0]
237 |
238 | def gains(self):
239 | x=self.values
240 | return [z for z in x if z>0]
241 |
242 | def avg_loss(self):
243 | return np.mean(self.losses())
244 |
245 | def avg_gain(self):
246 | return np.mean(self.gains())
247 |
248 | def drawdown(self):
249 | ## in case need numerous stats
250 | if "drawdownacc" not in dir(self):
251 | setattr(self, "drawdownacc", drawdown(cum_perc(self)))
252 | return self.drawdownacc
253 |
254 | def avg_drawdown(self):
255 | return self.perc_drawdown(50.0)
256 |
257 | def perc_drawdown(self, q):
258 | dd=self.drawdown()
259 | return np.percentile(dd, q)
260 |
261 | def worst_drawdown(self):
262 | dd=self.drawdown()
263 | return np.nanmin(dd.values)
264 |
265 | def time_in_drawdown(self):
266 | dd=self.drawdown()
267 | dd=[z for z in dd if not np.isnan(z)]
268 | in_dd=float(len([z for z in dd if z<0]))
269 | return in_dd/float(len(dd))
270 |
271 | def monthly_returns(self):
272 | return self.resample("1M", how="sum")
273 |
274 | def gaintolossratio(self):
275 | return self.avg_gain()/-self.avg_loss()
276 |
277 | def profitfactor(self):
278 | return sum(self.gains())/-sum(self.losses())
279 |
280 | def hitrate(self):
281 | no_gains=float(len(self.gains()))
282 | no_losses=float(len(self.losses()))
283 | return no_gains/(no_losses+no_gains)
284 |
285 |
286 | def cum_perc(pd_timeseries):
287 | """
288 | Cumulate percentage returns for a pandas time series
289 | """
290 |
291 | cum_datalist=[1+x for x in pd_timeseries]
292 | cum_datalist=pd.TimeSeries(cum_datalist, index=pd_timeseries.index)
293 |
294 |
295 | return cum_datalist.cumprod()
296 |
297 |
298 | def arbitrary_timeindex(Nperiods, index_start=pd.datetime(2000,1,1)):
299 | """
300 | For nice plotting, convert a list of prices or returns into an arbitrary pandas time series
301 | """
302 |
303 | ans=pd.bdate_range(start=index_start, periods=Nperiods)
304 |
305 | return ans
306 |
307 |
308 | def arbitrary_timeseries(datalist, index_start=pd.datetime(2000,1,1)):
309 | """
310 | For nice plotting, convert a list of prices or returns into an arbitrary pandas time series
311 | """
312 |
313 | ans=pd.TimeSeries(datalist, index=arbitrary_timeindex(len(datalist), index_start))
314 |
315 | return ans
316 |
317 | def remove_nans_from_list(xlist):
318 | return [x for x in xlist if not np.isnan(x)]
319 |
320 |
321 |
322 | def autocorr(x, t=1):
323 | return np.corrcoef(np.array([x[0:len(x)-t], x[t:len(x)]]))[0,1]
--------------------------------------------------------------------------------
/commonrandom.py:
--------------------------------------------------------------------------------
1 | """
2 | Functions used to create random data
3 | """
4 |
5 | from random import gauss
6 | import numpy as np
7 | import pandas as pd
8 | from common import DAYS_IN_YEAR, ROOT_DAYS_IN_YEAR, arbitrary_timeindex
9 | import scipy.signal as sg
10 |
11 | def generate_siney_trends(Nlength, Tlength , Xamplitude):
12 | """
13 | Generates a price process, Nlength returns, underlying trend with length T and amplitude X
14 | as a sine wave
15 |
16 | returns a vector of numbers as a list
17 |
18 | """
19 |
20 | halfAmplitude=Xamplitude/2.0
21 |
22 | cycles=Nlength/Tlength
23 | cycles_as_pi=cycles*np.pi
24 | increment=cycles_as_pi/Nlength
25 |
26 | alltrends=[np.sin(x)*halfAmplitude for x in np.arange(0.0, cycles_as_pi, increment)]
27 | alltrends=alltrends[:Nlength]
28 |
29 | return alltrends
30 |
31 |
32 | def generate_trends(Nlength, Tlength , Xamplitude):
33 | """
34 | Generates a price process, Nlength returns, underlying trend with length T and amplitude X
35 |
36 | returns a vector of numbers as a list
37 |
38 | """
39 |
40 | halfAmplitude=Xamplitude/2.0
41 | trend_step=Xamplitude/Tlength
42 |
43 | cycles=int(np.ceil(Nlength/Tlength))
44 |
45 | trendup=list(np.arange(start=-halfAmplitude, stop=halfAmplitude, step=trend_step))
46 | trenddown=list(np.arange(start=halfAmplitude, stop=-halfAmplitude, step=-trend_step))
47 | alltrends=[trendup+trenddown]*int(np.ceil(cycles))
48 | alltrends=sum(alltrends, [])
49 | alltrends=alltrends[:Nlength]
50 |
51 | return alltrends
52 |
53 |
54 |
55 |
56 | def generate_trendy_price(Nlength, Tlength , Xamplitude, Volscale, sines=False):
57 | """
58 | Generates a trend of length N amplitude X, plus gaussian noise mean zero std. dev (vol scale * amplitude)
59 |
60 | If sines=True then generates as a sine wave, otherwise straight line
61 |
62 | returns a vector of numbers
63 | """
64 |
65 | stdev=Volscale*Xamplitude
66 | noise=generate_noise(Nlength, stdev)
67 |
68 | ## Can use a different process here if desired
69 | if sines:
70 | process=generate_siney_trends(Nlength, Tlength , Xamplitude)
71 | else:
72 | process=generate_trends(Nlength, Tlength , Xamplitude)
73 |
74 | combined_price=[noise_item+process_item for (noise_item, process_item) in zip(noise, process)]
75 |
76 | return combined_price
77 |
78 | def generate_noise(Nlength, stdev):
79 | """
80 | Generates a series of gaussian noise as a list Nlength
81 | """
82 |
83 | return [gauss(0.0, stdev) for Unused in range(Nlength)]
84 |
85 |
86 |
87 |
88 | def threeassetportfolio(plength=5000, SRlist=[1.0, 1.0, 1.0], annual_vol=.15, clist=[.0,.0,.0], index_start=pd.datetime(2000,1,1)):
89 |
90 | (c1, c2, c3)=clist
91 | dindex=arbitrary_timeindex(plength, index_start)
92 |
93 | daily_vol=annual_vol/16.0
94 | means=[x*annual_vol/250.0 for x in SRlist]
95 | stds = np.diagflat([daily_vol]*3)
96 | corr=np.array([[1.0, c1, c2], [c1, 1.0, c3], [c2, c3, 1.0]])
97 |
98 | covs=np.dot(stds, np.dot(corr, stds))
99 | plength=len(dindex)
100 |
101 | m = np.random.multivariate_normal(means, covs, plength).T
102 |
103 | portreturns=pd.DataFrame(dict(one=m[0], two=m[1], three=m[2]), dindex)
104 | portreturns=portreturns[['one', 'two', 'three']]
105 |
106 | return portreturns
107 |
108 |
109 | def skew_returns_annualised(annualSR=1.0, want_skew=0.0, voltarget=0.20, size=10000):
110 | annual_rets=annualSR*voltarget
111 | daily_rets=annual_rets/DAYS_IN_YEAR
112 | daily_vol=voltarget/ROOT_DAYS_IN_YEAR
113 |
114 | return skew_returns(want_mean=daily_rets, want_stdev=daily_vol,want_skew=want_skew, size=size)
115 |
116 | def skew_returns(want_mean, want_stdev, want_skew, size=10000):
117 |
118 | EPSILON=0.0000001
119 | shapeparam=(2/(EPSILON+abs(want_skew)))**2
120 | scaleparam=want_stdev/(shapeparam)**.5
121 |
122 | sample = list(np.random.gamma(shapeparam, scaleparam, size=size))
123 |
124 | if want_skew<0.0:
125 | signadj=-1.0
126 | else:
127 | signadj=1.0
128 |
129 | natural_mean=shapeparam*scaleparam*signadj
130 | mean_adjustment=want_mean - natural_mean
131 |
132 | sample=[(x*signadj)+mean_adjustment for x in sample]
133 |
134 | return sample
135 |
136 | def autocorr_skewed_returns(rho, want_mean, want_stdev, want_skew, size=10000):
137 |
138 |
139 | ## closed form correction for ar1 process noise
140 | noise_stdev=(want_stdev**2 * (1-rho))**.5
141 | noise_terms=skew_returns(want_mean, noise_stdev, want_skew, size)
142 |
143 | ## combine the noise with a filter
144 | return sg.lfilter((1,),(1,-rho),noise_terms)
145 |
146 |
147 |
148 |
149 | def adj_moments_for_rho(want_rho, want_mean, want_skew, want_stdev):
150 | """
151 | Autocorrelation introduces biases into other moments of a distribution
152 |
153 | Here I correct for these
154 | """
155 | assert abs(want_rho)<=0.8
156 |
157 | mean_correction=1/(1-want_rho)
158 |
159 | if want_rho>=0.0:
160 | skew_correction=(1-want_rho)**.5
161 | else:
162 | skew_correction=np.interp(want_rho, [-0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -.2, -0.1],
163 | [.14, .27, .42, .58, .72, .84, .93, .98 ])
164 |
165 | ## somewhat hacky, but we do a correction inside the random generation function already
166 |
167 | stdev_correction=np.interp(want_rho, [-0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -.2, -0.1,
168 | 0.0,.1,.2,.3,.4,.5,.6,.7,.8],
169 | [2.24, 1.83, 1.58, 1.41, 1.29, 1.19, 1.12, 1.05,
170 | 1.0, .95,.91,.88 ,.85, .82 , .79,.77 ,.75])
171 |
172 | adj_want_stdev=want_stdev/stdev_correction
173 | adj_want_mean=want_mean/mean_correction
174 | adj_want_skew=want_skew/skew_correction
175 |
176 | return (adj_want_mean, adj_want_skew, adj_want_stdev)
--------------------------------------------------------------------------------
/config.csv:
--------------------------------------------------------------------------------
1 | Instrument,Pointsize
2 | CORN,50
3 | LEANHOG,400
4 | LIVECOW,400
5 | SOYBEAN,50
6 | WHEAT,50
7 | KR10,1000000
8 | KR3,1000000
9 | BOBL,1000
10 | BTP,1000
11 | BUND,1000
12 | OAT,1000
13 | SHATZ,1000
14 | US10,1000
15 | US2,2000
16 | US20,1000
17 | US5,1000
18 | V2X,100
19 | VIX,1000
20 | KOSPI,500000
21 | AEX,200
22 | CAC,10
23 | SMI,10
24 | NASDAQ,20
25 | SP500,50
26 | AUD,100000
27 | EUR,125000
28 | GBP,62500
29 | JPY,12500000
30 | MXP,500000
31 | NZD,100000
32 | COPPER,25000
33 | GOLD,100
34 | PALLAD,100
35 | PLAT,50
36 | CRUDE_W,1000
37 | GAS_US,10000
38 | EDOLLAR,2500
39 | EUROSTX,10
40 |
--------------------------------------------------------------------------------
/data/CAC_price.csv:
--------------------------------------------------------------------------------
1 | DATETIME,ADJ
2 | 2013-08-14,4013.307884615385
3 | 2013-08-15,3984.807884615385
4 | 2013-08-16,4010.307884615385
5 | 2013-08-19,3980.307884615385
6 | 2013-08-20,3925.307884615385
7 | 2013-08-21,3932.307884615385
8 | 2013-08-22,3955.807884615385
9 | 2013-08-23,3968.807884615385
10 | 2013-08-26,3945.807884615385
11 | 2013-08-27,3847.307884615385
12 | 2013-08-28,3848.807884615385
13 | 2013-08-29,3868.307884615385
14 | 2013-08-30,3824.307884615385
15 | 2013-09-02,3887.807884615385
16 | 2013-09-03,3866.807884615385
17 | 2013-09-04,3865.807884615385
18 | 2013-09-05,3898.307884615385
19 | 2013-09-06,3928.807884615385
20 | 2013-09-09,3938.307884615385
21 | 2013-09-10,4006.807884615385
22 | 2013-09-11,4015.807884615385
23 | 2013-09-12,4000.807884615385
24 | 2013-09-13,4011.807884615385
25 | 2013-09-16,4036.307884615385
26 | 2013-09-17,4044.807884615385
27 | 2013-09-18,4119.807884615385
28 | 2013-09-19,4093.807884615385
29 | 2013-09-20,4083.807884615385
30 | 2013-09-23,4073.807884615385
31 | 2013-09-24,4086.807884615385
32 | 2013-09-25,4081.807884615385
33 | 2013-09-26,4092.307884615385
34 | 2013-09-27,4085.307884615385
35 | 2013-09-30,4039.807884615385
36 | 2013-10-01,4088.307884615385
37 | 2013-10-02,4065.307884615385
38 | 2013-10-03,4018.807884615385
39 | 2013-10-04,4063.307884615385
40 | 2013-10-07,4049.807884615385
41 | 2013-10-08,4015.307884615385
42 | 2013-10-09,4032.807884615385
43 | 2013-10-10,4130.807884615385
44 | 2013-10-11,4125.807884615385
45 | 2013-10-14,4134.307884615385
46 | 2013-10-15,4133.807884615385
47 | 2013-10-16,4141.807884615385
48 | 2013-10-17,4145.307884615385
49 | 2013-10-18,4180.807884615385
50 | 2013-10-21,4171.307884615385
51 | 2013-10-22,4187.307884615385
52 | 2013-10-23,4160.807884615385
53 | 2013-10-24,4166.807884615385
54 | 2013-10-25,4168.807884615385
55 | 2013-10-28,4151.307884615385
56 | 2013-10-29,4173.807884615385
57 | 2013-10-30,4166.807884615385
58 | 2013-10-31,4193.307884615385
59 | 2013-11-01,4178.307884615385
60 | 2013-11-04,4195.307884615385
61 | 2013-11-05,4148.807884615385
62 | 2013-11-06,4182.807884615385
63 | 2013-11-07,4144.807884615385
64 | 2013-11-08,4169.307884615385
65 | 2013-11-11,4189.307884615385
66 | 2013-11-12,4161.807884615385
67 | 2013-11-13,4166.307884615385
68 | 2013-11-14,4179.807884615385
69 | 2013-11-15,4195.3125
70 | 2013-11-18,4203.8125
71 | 2013-11-19,4165.3125
72 | 2013-11-20,4150.8125
73 | 2013-11-21,4168.3125
74 | 2013-11-22,4181.3125
75 | 2013-11-25,4203.3125
76 | 2013-11-26,4180.8125
77 | 2013-11-27,4195.8125
78 | 2013-11-28,4205.8125
79 | 2013-11-29,4198.8125
80 | 2013-12-02,4189.8125
81 | 2013-12-03,4076.8125
82 | 2013-12-04,4051.8125
83 | 2013-12-05,4004.8125
84 | 2013-12-06,4035.8125
85 | 2013-12-09,4039.3125
86 | 2013-12-10,3997.3125
87 | 2013-12-11,3992.8125
88 | 2013-12-12,3975.3125
89 | 2013-12-13,3965.3125
90 | 2013-12-16,4030.3125
91 | 2013-12-17,3981.8125
92 | 2013-12-18,4021.375
93 | 2013-12-19,4088.875
94 | 2013-12-20,4106.375
95 | 2013-12-23,4129.875
96 | 2013-12-24,4132.375
97 | 2013-12-25,
98 | 2013-12-26,
99 | 2013-12-27,4190.875
100 | 2013-12-30,4188.375
101 | 2013-12-31,4208.375
102 | 2014-01-01,
103 | 2014-01-02,4136.875
104 | 2014-01-03,4156.875
105 | 2014-01-06,4138.375
106 | 2014-01-07,4170.875
107 | 2014-01-08,4170.375
108 | 2014-01-09,4138.375
109 | 2014-01-10,4161.875
110 | 2014-01-13,4173.375
111 | 2014-01-14,4183.375
112 | 2014-01-15,4240.375
113 | 2014-01-16,4229.375
114 | 2014-01-17,4239.9375
115 | 2014-01-20,4234.9375
116 | 2014-01-21,4235.9375
117 | 2014-01-22,4238.4375
118 | 2014-01-23,4193.9375
119 | 2014-01-24,4073.9375
120 | 2014-01-27,4055.9375
121 | 2014-01-28,4096.9375
122 | 2014-01-29,4068.9375
123 | 2014-01-30,4091.9375
124 | 2014-01-31,4078.4375
125 | 2014-02-03,4019.9375
126 | 2014-02-04,4028.9375
127 | 2014-02-05,4030.4375
128 | 2014-02-06,4100.4375
129 | 2014-02-07,4139.9375
130 | 2014-02-10,4147.9375
131 | 2014-02-11,4195.9375
132 | 2014-02-12,4216.9375
133 | 2014-02-13,4225.4375
134 | 2014-02-14,4251.4375
135 | 2014-02-17,4247.9375
136 | 2014-02-18,4243.9375
137 | 2014-02-19,4251.9375
138 | 2014-02-20,4267.4375
139 | 2014-02-21,4294.0
140 | 2014-02-24,4330.0
141 | 2014-02-25,4327.5
142 | 2014-02-26,4312.0
143 | 2014-02-27,4310.5
144 | 2014-02-28,4316.5
145 | 2014-03-03,4201.5
146 | 2014-03-04,4307.0
147 | 2014-03-05,4301.0
148 | 2014-03-06,4328.5
149 | 2014-03-07,4281.166666666667
150 | 2014-03-10,4294.0
151 | 2014-03-11,4254.0
152 | 2014-03-12,4225.5
153 | 2014-03-13,4138.5
154 | 2014-03-14,4130.6875
155 | 2014-03-17,4180.5
156 | 2014-03-18,4238.5
157 | 2014-03-19,4187.0
158 | 2014-03-20,4236.0
159 | 2014-03-21,4210.0
160 | 2014-03-24,
161 | 2014-03-25,4276.0
162 | 2014-03-26,4285.0
163 | 2014-03-27,4299.0
164 | 2014-03-28,4319.0
165 | 2014-03-31,4315.5
166 | 2014-04-01,4348.5
167 | 2014-04-02,4349.0
168 | 2014-04-03,4363.0
169 | 2014-04-04,
170 | 2014-04-07,4351.0
171 | 2014-04-08,4364.8125
172 | 2014-04-09,
173 | 2014-04-10,4284.0
174 | 2014-04-11,4262.0
175 | 2014-04-14,4292.5
176 | 2014-04-15,4287.0
177 | 2014-04-16,4331.0
178 | 2014-04-17,4363.5
179 | 2014-04-18,
180 | 2014-04-21,
181 | 2014-04-22,4399.0
182 | 2014-04-23,4371.0
183 | 2014-04-24,4379.25
184 | 2014-04-25,4371.5
185 | 2014-04-28,4372.6875
186 | 2014-04-29,
187 | 2014-04-30,4420.0
188 | 2014-05-01,
189 | 2014-05-02,4397.5
190 | 2014-05-05,4408.0
191 | 2014-05-06,4361.0
192 | 2014-05-07,4398.5
193 | 2014-05-08,4432.0
194 | 2014-05-09,4429.0
195 | 2014-05-12,4464.5
196 | 2014-05-13,4476.5
197 | 2014-05-14,4465.5
198 | 2014-05-15,4433.5
199 | 2014-05-16,4438.5
200 | 2014-05-19,4450.0
201 | 2014-05-20,4428.0
202 | 2014-05-21,4459.0
203 | 2014-05-22,4465.0
204 | 2014-05-23,4482.0
205 | 2014-05-26,4508.0
206 | 2014-05-27,4527.625
207 | 2014-05-28,4517.5
208 | 2014-05-29,4529.5
209 | 2014-05-30,4523.25
210 | 2014-06-02,4529.5
211 | 2014-06-03,4514.0
212 | 2014-06-04,4489.25
213 | 2014-06-05,4567.0
214 | 2014-06-06,4602.5
215 | 2014-06-09,4606.5
216 | 2014-06-10,4606.0
217 | 2014-06-11,4570.0
218 | 2014-06-12,4564.25
219 | 2014-06-13,4557.0
220 | 2014-06-16,4523.0
221 | 2014-06-17,4542.5
222 | 2014-06-18,4574.5
223 | 2014-06-19,4575.0
224 | 2014-06-20,4548.5
225 | 2014-06-23,4537.5
226 | 2014-06-24,4534.75
227 | 2014-06-25,4488.0
228 | 2014-06-26,4474.5
229 | 2014-06-27,4466.5
230 | 2014-06-30,4439.0
231 | 2014-07-01,4476.0
232 | 2014-07-02,4461.0
233 | 2014-07-03,4487.75
234 | 2014-07-04,4490.0
235 | 2014-07-07,4433.0
236 | 2014-07-08,4369.5
237 | 2014-07-09,4383.0
238 | 2014-07-10,4334.0
239 | 2014-07-11,4343.0
240 | 2014-07-14,4358.0
241 | 2014-07-15,4333.5
242 | 2014-07-16,4386.5
243 | 2014-07-17,4301.0
244 | 2014-07-18,4361.5
245 | 2014-07-21,4329.5
246 | 2014-07-22,4383.5
247 | 2014-07-23,4391.0
248 | 2014-07-24,4418.0
249 | 2014-07-25,4346.25
250 | 2014-07-28,4366.5
251 | 2014-07-29,4385.25
252 | 2014-07-30,4330.75
253 | 2014-07-31,4298.5
254 | 2014-08-01,
255 | 2014-08-04,
256 | 2014-08-05,
257 | 2014-08-06,4212.5
258 | 2014-08-07,4191.75
259 | 2014-08-08,4152.25
260 | 2014-08-11,4198.5
261 | 2014-08-12,4187.0
262 | 2014-08-13,4209.5
263 | 2014-08-14,4231.0
264 | 2014-08-15,4211.5
265 | 2014-08-18,4251.5
266 | 2014-08-19,4274.0
267 | 2014-08-20,4262.0
268 | 2014-08-21,4300.0
269 | 2014-08-22,4270.5
270 | 2014-08-25,4350.5
271 | 2014-08-26,4397.5
272 | 2014-08-27,4408.0
273 | 2014-08-28,4386.0
274 | 2014-08-29,4401.5
275 | 2014-09-01,4392.25
276 | 2014-09-02,4395.0
277 | 2014-09-03,4431.5
278 | 2014-09-04,4500.0
279 | 2014-09-05,4512.5
280 | 2014-09-08,4483.5
281 | 2014-09-09,4457.0
282 | 2014-09-10,4483.5
283 | 2014-09-11,4461.0
284 | 2014-09-12,4454.5
285 | 2014-09-15,4449.0
286 | 2014-09-16,4450.5
287 | 2014-09-17,4459.5
288 | 2014-09-18,4470.0
289 | 2014-09-19,4472.5
290 | 2014-09-22,4452.5
291 | 2014-09-23,4381.5
292 | 2014-09-24,4448.0
293 | 2014-09-25,4371.5
294 | 2014-09-26,4441.5
295 | 2014-09-29,4384.5
296 | 2014-09-30,4426.5
297 | 2014-10-01,4370.5
298 | 2014-10-02,4287.0
299 | 2014-10-03,4314.5
300 | 2014-10-06,4308.25
301 | 2014-10-07,4202.0
302 | 2014-10-08,4244.5
303 | 2014-10-09,4128.5
304 | 2014-10-10,4098.0
305 | 2014-10-13,4067.0
306 | 2014-10-14,4091.0
307 | 2014-10-15,3995.5
308 | 2014-10-16,3922.25
309 | 2014-10-17,4040.5
310 | 2014-10-20,4033.0
311 | 2014-10-21,4126.0
312 | 2014-10-22,4094.5
313 | 2014-10-23,4177.5
314 | 2014-10-24,4173.5
315 | 2014-10-27,4142.5
316 | 2014-10-28,4163.5
317 | 2014-10-29,4143.5
318 | 2014-10-30,4185.5
319 | 2014-10-31,4268.5
320 | 2014-11-03,4221.0
321 | 2014-11-04,4179.5
322 | 2014-11-05,4234.5
323 | 2014-11-06,4267.5
324 | 2014-11-07,4221.0
325 | 2014-11-10,4252.0
326 | 2014-11-11,4275.5
327 | 2014-11-12,4228.0
328 | 2014-11-13,4226.0
329 | 2014-11-14,4233.5
330 | 2014-11-17,4266.0
331 | 2014-11-18,4310.0
332 | 2014-11-19,4304.5
333 | 2014-11-20,4265.5
334 | 2014-11-21,4380.5
335 | 2014-11-24,4415.5
336 | 2014-11-25,4425.5
337 | 2014-11-26,4419.5
338 | 2014-11-27,4414.5
339 | 2014-11-28,4417.5
340 | 2014-12-01,4417.0
341 | 2014-12-02,4432.0
342 | 2014-12-03,4439.0
343 | 2014-12-04,4388.5
344 | 2014-12-05,4447.5
345 | 2014-12-08,4399.5
346 | 2014-12-09,4337.5
347 | 2014-12-10,4241.5
348 | 2014-12-11,4237.5
349 | 2014-12-12,4124.5
350 | 2014-12-15,4055.5
351 | 2014-12-16,4087.5
352 | 2014-12-17,4202.5
353 | 2014-12-18,4310.0
354 | 2014-12-19,4253.5
355 | 2014-12-22,4258.0
356 | 2014-12-23,4301.0
357 | 2014-12-24,4287.0
358 | 2014-12-25,
359 | 2014-12-26,
360 | 2014-12-29,4309.5
361 | 2014-12-30,4245.5
362 | 2014-12-31,4258.0
363 | 2015-01-01,
364 | 2015-01-02,4229.5
365 | 2015-01-05,4095.5
366 | 2015-01-06,4083.5
367 | 2015-01-07,4125.0
368 | 2015-01-08,4232.5
369 | 2015-01-09,4171.0
370 | 2015-01-12,4189.5
371 | 2015-01-13,4234.5
372 | 2015-01-14,4228.5
373 | 2015-01-15,4296.5
374 | 2015-01-16,4342.5
375 | 2015-01-19,4320.0
376 | 2015-01-20,4393.0
377 | 2015-01-21,4417.5
378 | 2015-01-22,4539.0
379 | 2015-01-23,4569.0
380 | 2015-01-26,4621.5
381 | 2015-01-27,4567.0
382 | 2015-01-28,4484.0
383 | 2015-01-29,4602.0
384 | 2015-01-30,4529.0
385 | 2015-02-02,4582.0
386 | 2015-02-03,4629.0
387 | 2015-02-04,4620.5
388 | 2015-02-05,4635.0
389 | 2015-02-06,4585.5
390 | 2015-02-09,4581.5
391 | 2015-02-10,4646.5
392 | 2015-02-11,4625.5
393 | 2015-02-12,4681.0
394 | 2015-02-13,4701.5
395 | 2015-02-16,4655.0
396 | 2015-02-17,4706.0
397 | 2015-02-18,4732.5
398 | 2015-02-19,4757.5
399 | 2015-02-20,4816.0
400 | 2015-02-23,4782.5
401 | 2015-02-24,4821.5
402 | 2015-02-25,4810.5
403 | 2015-02-26,4836.5
404 | 2015-02-27,4874.75
405 | 2015-03-02,4861.5
406 | 2015-03-03,4820.5
407 | 2015-03-04,4850.0
408 | 2015-03-05,4890.5
409 | 2015-03-06,4873.5
410 | 2015-03-09,4870.5
411 | 2015-03-10,4820.5
412 | 2015-03-11,4933.0
413 | 2015-03-12,4931.0
414 | 2015-03-13,4951.0
415 | 2015-03-16,4998.0
416 | 2015-03-17,4977.5
417 | 2015-03-18,4953.5
418 | 2015-03-19,4962.5
419 | 2015-03-20,5020.5
420 | 2015-03-23,4979.5
421 | 2015-03-24,5011.5
422 | 2015-03-25,4949.0
423 | 2015-03-26,4943.0
424 | 2015-03-27,4981.0
425 | 2015-03-30,5022.5
426 | 2015-03-31,4964.0
427 | 2015-04-01,5010.5
428 | 2015-04-02,5016.0
429 | 2015-04-03,
430 | 2015-04-06,
431 | 2015-04-07,5085.5
432 | 2015-04-08,5086.0
433 | 2015-04-09,5166.5
434 | 2015-04-10,5182.5
435 | 2015-04-13,5167.0
436 | 2015-04-14,5168.5
437 | 2015-04-15,5190.0
438 | 2015-04-16,5157.0
439 | 2015-04-17,5071.5
440 | 2015-04-20,5119.0
441 | 2015-04-21,5145.5
442 | 2015-04-22,5154.75
443 |
--------------------------------------------------------------------------------
/ewmac.py:
--------------------------------------------------------------------------------
1 | """
2 | This file shows how to calculate the EWMAC trading rule for crude oil futures
3 |
4 | As in chapter 7 / appendix B of "Systematic Trading" by Robert Carver (www.systematictrading.org)
5 |
6 | Required: pandas, matplotlib
7 |
8 | USE AT YOUR OWN RISK! No warranty is provided or implied.
9 |
10 | Handling of NAN's and Inf's isn't done here (except within pandas),
11 | And there is no error handling!
12 |
13 | """
14 |
15 | import pandas as pd
16 | import matplotlib.pyplot as plt
17 | from common import pd_readcsv, cap_series, ewmac_forecast_scalar, get_price_for_instrument
18 |
19 |
20 | """
21 | get some data
22 | """
23 |
24 | ## This is the stitched price series
25 | ## We can't use the price of the contract we're trading, or the volatility will be jumpy
26 | ## And we'll miss out on the rolldown. See http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html
27 | code="CRUDE_W"
28 | price=get_price_for_instrument(code)
29 |
30 |
31 | ## Shouldn't need changing
32 | vol_lookback=25
33 |
34 | """
35 | Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback
36 |
37 | Assumes that 'price' is daily data
38 | """
39 |
40 | Lfast=16
41 | Lslow=4*Lfast
42 |
43 | d1=pd.datetime(2007,1,1)
44 | d2=pd.datetime(2009,12,31)
45 |
46 | ## We don't need to calculate the decay parameter, just use the span directly
47 |
48 | fast_ewma=pd.ewma(price, span=Lfast)
49 | slow_ewma=pd.ewma(price, span=Lslow)
50 | raw_ewmac=fast_ewma - slow_ewma
51 |
52 | data_to_plot=pd.concat([price, fast_ewma, slow_ewma], axis=1)
53 | data_to_plot.columns=['Price', 'Fast', 'Slow']
54 |
55 | data_to_plot[d1:d2].plot()
56 | plt.show()
57 |
58 | raw_ewmac[d1:d2].plot()
59 | plt.title("Raw EWMAC")
60 | plt.show()
61 |
62 | ## volatility adjustment
63 | stdev_returns=pd.ewmstd(price - price.shift(1), span=vol_lookback)
64 | vol_adj_ewmac=raw_ewmac/stdev_returns
65 |
66 | vol_adj_ewmac[d1:d2].plot()
67 | plt.title("Vol adjusted")
68 | plt.show()
69 |
70 | ## scaling adjustment
71 | f_scalar=ewmac_forecast_scalar(Lfast, Lslow)
72 |
73 | forecast=vol_adj_ewmac*f_scalar
74 |
75 |
76 | cap_forecast=cap_series(forecast, capmin=-20.0,capmax=20.0)
77 |
78 | data_to_plot=pd.concat([forecast, cap_forecast], axis=1)
79 | data_to_plot.columns=['Scaled Forecast', 'Capped forecast']
80 |
81 | data_to_plot[d1:d2].plot()
82 | plt.show()
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/fed_10yearinflation_US.csv:
--------------------------------------------------------------------------------
1 | Date,Value
2 | 01/01/03,2.29
3 | 01/02/03,1.99
4 | 01/03/03,1.94
5 | 01/04/03,2.18
6 | 01/05/03,1.91
7 | 01/06/03,1.72
8 | 01/07/03,2.11
9 | 01/08/03,2.32
10 | 01/09/03,2.19
11 | 01/10/03,2.08
12 | 01/11/03,1.96
13 | 01/12/03,1.98
14 | 01/01/04,1.89
15 | 01/02/04,1.76
16 | 01/03/04,1.47
17 | 01/04/04,1.9
18 | 01/05/04,2.09
19 | 01/06/04,2.15
20 | 01/07/04,2.02
21 | 01/08/04,1.86
22 | 01/09/04,1.8
23 | 01/10/04,1.73
24 | 01/11/04,1.68
25 | 01/12/04,1.67
26 | 01/01/05,1.72
27 | 01/02/05,1.63
28 | 01/03/05,1.79
29 | 01/04/05,1.71
30 | 01/05/05,1.65
31 | 01/06/05,1.67
32 | 01/07/05,1.88
33 | 01/08/05,1.89
34 | 01/09/05,1.7
35 | 01/10/05,1.94
36 | 01/11/05,2.06
37 | 01/12/05,2.12
38 | 01/01/06,2.01
39 | 01/02/06,2.05
40 | 01/03/06,2.2
41 | 01/04/06,2.41
42 | 01/05/06,2.45
43 | 01/06/06,2.53
44 | 01/07/06,2.51
45 | 01/08/06,2.29
46 | 01/09/06,2.32
47 | 01/10/06,2.41
48 | 01/11/06,2.29
49 | 01/12/06,2.25
50 | 01/01/07,2.44
51 | 01/02/07,2.36
52 | 01/03/07,2.18
53 | 01/04/07,2.26
54 | 01/05/07,2.37
55 | 01/06/07,2.69
56 | 01/07/07,2.64
57 | 01/08/07,2.44
58 | 01/09/07,2.26
59 | 01/10/07,2.2
60 | 01/11/07,1.77
61 | 01/12/07,1.79
62 | 01/01/08,1.47
63 | 01/02/08,1.41
64 | 01/03/08,1.09
65 | 01/04/08,1.36
66 | 01/05/08,1.46
67 | 01/06/08,1.63
68 | 01/07/08,1.57
69 | 01/08/08,1.68
70 | 01/09/08,1.85
71 | 01/10/08,2.75
72 | 01/11/08,2.89
73 | 01/12/08,2.17
74 | 01/01/09,1.91
75 | 01/02/09,1.75
76 | 01/03/09,1.71
77 | 01/04/09,1.57
78 | 01/05/09,1.72
79 | 01/06/09,1.86
80 | 01/07/09,1.82
81 | 01/08/09,1.77
82 | 01/09/09,1.64
83 | 01/10/09,1.48
84 | 01/11/09,1.28
85 | 01/12/09,1.36
86 | 01/01/10,1.37
87 | 01/02/10,1.42
88 | 01/03/10,1.51
89 | 01/04/10,1.5
90 | 01/05/10,1.31
91 | 01/06/10,1.26
92 | 01/07/10,1.24
93 | 01/08/10,1.02
94 | 01/09/10,0.91
95 | 01/10/10,0.53
96 | 01/11/10,0.67
97 | 01/12/10,1.04
98 | 01/01/11,1.06
99 | 01/02/11,1.24
100 | 01/03/11,0.96
101 | 01/04/11,0.86
102 | 01/05/11,0.78
103 | 01/06/11,0.76
104 | 01/07/11,0.62
105 | 01/08/11,0.14
106 | 01/09/11,0.08
107 | 01/10/11,0.19
108 | 01/11/11,0
109 | 01/12/11,-0.03
110 | 01/01/12,-0.11
111 | 01/02/12,-0.25
112 | 01/03/12,-0.14
113 | 01/04/12,-0.21
114 | 01/05/12,-0.34
115 | 01/06/12,-0.5
116 | 01/07/12,-0.6
117 | 01/08/12,-0.59
118 | 01/09/12,-0.71
119 | 01/10/12,-0.75
120 | 01/11/12,-0.77
121 | 01/12/12,-0.76
122 | 01/01/13,-0.61
123 | 01/02/13,-0.57
124 | 01/03/13,-0.59
125 | 01/04/13,-0.65
126 | 01/05/13,-0.36
127 | 01/06/13,0.25
128 | 01/07/13,0.46
129 | 01/08/13,0.55
130 | 01/09/13,0.66
131 | 01/10/13,0.43
132 | 01/11/13,0.55
133 | 01/12/13,0.74
134 | 01/01/14,0.63
135 | 01/02/14,0.55
136 | 01/03/14,0.56
137 | 01/04/14,0.54
138 | 01/05/14,0.37
139 | 01/06/14,0.37
140 | 01/07/14,0.28
141 | 01/08/14,0.22
142 | 01/09/14,0.46
143 | 01/10/14,0.38
144 | 01/11/14,0.45
145 | 01/12/14,0.51
146 | 01/01/15,0.27
147 | 01/02/15,0.26
148 | 01/03/15,0.28
149 | 01/04/15,0.08
150 | 01/05/15,0.33
151 | 01/06/15,0.5
152 | 01/07/15,0.5
153 | 01/08/15,0.56
154 | 01/09/15,0.65
155 | 01/10/15,0.57
156 | 01/11/15,0.69
157 | 01/12/15,0.73
158 | 01/01/16,0.67
159 | 01/02/16,0.47
160 | 01/03/16,0.34
161 | 01/04/16,0.19
162 | 01/05/16,0.21
163 |
--------------------------------------------------------------------------------
/google-chrome-stable_current_i386.deb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robcarver17/systematictradingexamples/34b3a457811fa2d7be1955028f10fdb823c656a1/google-chrome-stable_current_i386.deb
--------------------------------------------------------------------------------
/handcraftweights.csv:
--------------------------------------------------------------------------------
1 | c1,c2,c3,w1,w2,w3
2 | 0.9,0.9,0.9,0.333,0.333,0.333
3 | 0.9,0.9,0.75,0.26,0.37,0.37
4 | 0.9,0.9,0.5,0.16,0.42,0.42
5 | 0.9,0.9,0.25,0.2,0.4,0.4
6 | 0.9,0.9,0,0.22,0.39,0.39
7 | 0.9,0.75,0.9,0.37,0.26,0.37
8 | 0.9,0.75,0.75,0.3,0.3,0.4
9 | 0.9,0.75,0.5,0.23,0.35,0.42
10 | 0.9,0.75,0.25,0.14,0.43,0.43
11 | 0.9,0.75,0,0.16,0.42,0.42
12 | 0.9,0.5,0.9,0.42,0.16,0.42
13 | 0.9,0.5,0.75,0.35,0.23,0.42
14 | 0.9,0.5,0.5,0.29,0.29,0.42
15 | 0.9,0.5,0.25,0.2,0.37,0.43
16 | 0.9,0.5,0,0.1,0.45,0.45
17 | 0.9,0.25,0.9,0.4,0.2,0.4
18 | 0.9,0.25,0.75,0.43,0.13,0.43
19 | 0.9,0.25,0.5,0.37,0.2,0.43
20 | 0.9,0.25,0.25,0.28,0.28,0.44
21 | 0.9,0.25,0,0.19,0.36,0.45
22 | 0.9,0,0.9,0.39,0.22,0.39
23 | 0.9,0,0.75,0.42,0.16,0.42
24 | 0.9,0,0.5,0.45,0.1,0.45
25 | 0.9,0,0.25,0.36,0.19,0.45
26 | 0.9,0,0,0.27,0.27,0.46
27 | 0.75,0.9,0.9,0.37,0.37,0.26
28 | 0.75,0.9,0.75,0.3,0.4,0.3
29 | 0.75,0.9,0.5,0.23,0.42,0.35
30 | 0.75,0.9,0.25,0.14,0.43,0.43
31 | 0.75,0.9,0,0.16,0.42,0.42
32 | 0.75,0.75,0.9,0.4,0.4,0.4
33 | 0.75,0.75,0.75,0.333,0.333,0.333
34 | 0.75,0.75,0.5,0.24,0.38,0.38
35 | 0.75,0.75,0.25,0.2,0.4,0.4
36 | 0.75,0.75,0,0.2,0.4,0.4
37 | 0.75,0.5,0.9,0.42,0.23,0.35
38 | 0.75,0.5,0.75,0.38,0.24,0.38
39 | 0.75,0.5,0.5,0.31,0.31,0.38
40 | 0.75,0.5,0.25,0.28,0.33,0.39
41 | 0.75,0.5,0,0.18,0.41,0.41
42 | 0.75,0.25,0.9,0.43,0.14,0.43
43 | 0.75,0.25,0.75,0.4,0.2,0.4
44 | 0.75,0.25,0.5,0.41,0.18,0.41
45 | 0.75,0.25,0.25,0.29,0.29,0.42
46 | 0.75,0.25,0,0.2,0.37,0.43
47 | 0.75,0,0.9,0.42,0.16,0.42
48 | 0.75,0,0.75,0.4,0.2,0.4
49 | 0.75,0,0.5,0.41,0.18,0.41
50 | 0.75,0,0.25,0.38,0.2,0.42
51 | 0.75,0,0,0.29,0.29,0.42
52 | 0.5,0.9,0.9,0.42,0.42,0.16
53 | 0.5,0.9,0.75,0.35,0.42,0.23
54 | 0.5,0.9,0.5,0.29,0.42,0.29
55 | 0.5,0.9,0.25,0.2,0.43,0.37
56 | 0.5,0.9,0,0.1,0.45,0.45
57 | 0.5,0.75,0.9,0.42,0.35,0.23
58 | 0.5,0.75,0.75,0.38,0.38,0.24
59 | 0.5,0.75,0.5,0.31,0.38,0.31
60 | 0.5,0.75,0.25,0.28,0.39,0.33
61 | 0.5,0.75,0,0.18,0.41,0.41
62 | 0.5,0.5,0.9,0.42,0.29,0.29
63 | 0.5,0.5,0.75,0.38,0.31,0.31
64 | 0.5,0.5,0.5,0.333,0.333,0.333
65 | 0.5,0.5,0.25,0.3,0.35,0.35
66 | 0.5,0.5,0,0.26,0.37,0.37
67 | 0.5,0.25,0.9,0.3,0.35,0.35
68 | 0.5,0.25,0.75,0.39,0.28,0.33
69 | 0.5,0.25,0.5,0.35,0.3,0.35
70 | 0.5,0.25,0.25,0.32,0.32,0.36
71 | 0.5,0.25,0,0.28,0.34,0.38
72 | 0.5,0,0.9,0.45,0.1,0.45
73 | 0.5,0,0.75,0.41,0.18,0.41
74 | 0.5,0,0.5,0.37,0.26,0.37
75 | 0.5,0,0.25,0.34,0.28,0.38
76 | 0.5,0,0,0.3,0.3,0.4
77 | 0.25,0.9,0.9,0.4,0.4,0.2
78 | 0.25,0.9,0.75,0.43,0.43,0.14
79 | 0.25,0.9,0.5,0.37,0.43,0.2
80 | 0.25,0.9,0.25,0.28,0.44,0.28
81 | 0.25,0.9,0,0.22,0.39,0.39
82 | 0.25,0.75,0.9,0.43,0.43,0.14
83 | 0.25,0.75,0.75,0.39,0.39,0.22
84 | 0.25,0.75,0.5,0.33,0.39,0.28
85 | 0.25,0.75,0.25,0.3,0.4,0.3
86 | 0.25,0.75,0,0.2,0.42,0.38
87 | 0.25,0.5,0.9,0.43,0.37,0.2
88 | 0.25,0.5,0.75,0.39,0.33,0.28
89 | 0.25,0.5,0.5,0.35,0.35,0.3
90 | 0.25,0.5,0.25,0.32,0.36,0.32
91 | 0.25,0.5,0,0.28,0.38,0.34
92 | 0.25,0.25,0.9,0.44,0.28,0.28
93 | 0.25,0.25,0.75,0.4,0.3,0.3
94 | 0.25,0.25,0.5,0.36,0.32,0.32
95 | 0.25,0.25,0.25,0.333,0.333,0.333
96 | 0.25,0.25,0,0.3,0.35,0.35
97 | 0.25,0,0.9,0.45,0.19,0.36
98 | 0.25,0,0.75,0.42,0.2,0.38
99 | 0.25,0,0.5,0.38,0.28,0.34
100 | 0.25,0,0.25,0.35,0.3,0.35
101 | 0.25,0,0,0.32,0.32,0.36
102 | 0,0.9,0.9,0.45,0.36,0.19
103 | 0,0.9,0.75,0.42,0.42,0.16
104 | 0,0.9,0.5,0.45,0.45,0.1
105 | 0,0.9,0.25,0.36,0.45,0.19
106 | 0,0.9,0,0.27,0.46,0.27
107 | 0,0.75,0.9,0.42,0.42,0.16
108 | 0,0.75,0.75,0.4,0.4,0.2
109 | 0,0.75,0.5,0.41,0.41,0.18
110 | 0,0.75,0.25,0.38,0.42,0.2
111 | 0,0.75,0,0.29,0.42,0.29
112 | 0,0.5,0.9,0.45,0.45,0.1
113 | 0,0.5,0.75,0.41,0.41,0.18
114 | 0,0.5,0.5,0.37,0.37,0.26
115 | 0,0.5,0.25,0.34,0.38,0.28
116 | 0,0.5,0,0.3,0.4,0.3
117 | 0,0.25,0.9,0.45,0.36,0.19
118 | 0,0.25,0.75,0.42,0.38,0.2
119 | 0,0.25,0.5,0.38,0.34,0.28
120 | 0,0.25,0.25,0.35,0.35,0.3
121 | 0,0.25,0,0.32,0.36,0.32
122 | 0,0,0.9,0.46,0.27,0.27
123 | 0,0,0.75,0.42,0.29,0.29
124 | 0,0,0.5,0.4,0.3,0.3
125 | 0,0,0.25,0.36,0.32,0.32
126 | 0,0,0,0.333,0.333,0.333
127 |
--------------------------------------------------------------------------------
/investigateforecasts.py:
--------------------------------------------------------------------------------
1 | """
2 | Here we'll check the hypothesis that we should use continous, rather than binary, forecasts
3 |
4 | """
5 |
6 | """
7 | First job - collect a stack of pooled data
8 |
9 | We're going to be plotting the forecast value versus the realised normalised return
10 |
11 | """
12 |
13 | from tradingrules import calc_ewmac_forecast, volatility, calc_carry_forecast
14 | from common import get_price_for_instrument, get_list_code, cap_series, get_point_sizes, ROOT_DAYS_IN_YEAR, calculate_pandl, sharpe, get_carry_data, stack_ts
15 | import pandas as pd
16 | import numpy as np
17 | import matplotlib.pyplot as plt
18 | from scipy.stats import ttest_ind
19 |
20 | def make_binary(xseries, rounded=False):
21 | """
22 | Turn a forecast into a binary version +10, -10
23 |
24 | We do this in two ways:
25 |
26 | - true binary
27 | - rounded binary. If abs(x)<5, forecast is zero. Else forecast is
28 | """
29 |
30 | if rounded:
31 | (min_value, max_value)=(-5.0, 5.0)
32 | else:
33 | (min_value, max_value)=(0.0, 0.0)
34 |
35 | return xseries.apply(make_binary_row, args=(min_value, max_value))
36 |
37 |
38 | def make_binary_row(x, min_value,max_value):
39 | """
40 | Binarise forecast
41 | """
42 |
43 | if xmax_value:
46 | return 10.0
47 |
48 | return 0.0
49 |
50 |
51 |
52 | ## If you set Lfast to None, will use carry rule
53 | Lfast=None
54 |
55 |
56 | code_list=get_list_code()
57 |
58 | priceStack=[]
59 | currentpriceStack=[]
60 | fcastStack=[]
61 | binary1Stack=[]
62 | binary2Stack=[]
63 | vnStack=[]
64 | volStack=[]
65 | root_days_in_week=5**.5
66 |
67 | for code in code_list:
68 | price=get_price_for_instrument(code)
69 | carrydata=get_carry_data(code)
70 | current_price=carrydata.TRADED
71 |
72 | if Lfast is None:
73 | forecast=calc_carry_forecast(carrydata, price)
74 | ## carry
75 | else:
76 | forecast=calc_ewmac_forecast(price, Lfast)
77 |
78 | stdev=volatility(price)*root_days_in_week
79 | price_volatility=100*stdev/current_price
80 | next_weeks_return =price.shift(-5) - price
81 | next_weeks_vol_norm_return =next_weeks_return/stdev
82 | next_weeks_vol_norm_return=cap_series(next_weeks_vol_norm_return, capmin=-50.0,capmax=50.0)
83 |
84 | fcastStack.append(forecast)
85 | vnStack.append(next_weeks_vol_norm_return)
86 |
87 | binary1forecast=make_binary(forecast, rounded=False)
88 | binary2forecast=make_binary(forecast, rounded=True)
89 |
90 | binary1Stack.append(binary1forecast)
91 | binary2Stack.append(binary2forecast)
92 |
93 | volStack.append(price_volatility)
94 |
95 | priceStack.append(price)
96 | currentpriceStack.append(current_price)
97 |
98 | fcastStack_all=pd.concat(fcastStack, axis=0).values
99 | vnStack_all=pd.concat(vnStack, axis=0).values
100 |
101 | conditionals=[]
102 | fcastpoints=[-20.0, -10.0, 0.0, 10.0, 20.0]
103 |
104 | for idx in range(len(fcastpoints))[1:]:
105 | condp=[vnStack_all[i] for i in range(len(vnStack_all)) if fcastStack_all[i]>=fcastpoints[idx-1] and fcastStack_all[i]0:
41 | return 1.0
42 |
43 |
44 | def calc_cash_weights(risk_weights):
45 | cash_weights=[rw/vl for (rw, vl) in zip(risk_weights, vols)]
46 | adj_wts=sum(cash_weights)/sum(risk_weights)
47 | cash_weights=[wt/adj_wts for wt in cash_weights]
48 | return cash_weights
49 |
50 | def ts_calc_cash_weights(risk_weights_ts):
51 | return risk_weights_ts.apply(calc_cash_weights, 1)
52 |
53 | def risk_weights_with_model(SRdiffs, risk_weights, SRdifftable, multipliers, absolute=False):
54 |
55 | multiplier_equity=np.interp(SRdiffs[0], SRdifftable, multipliers)
56 | multipliers_bonds=np.interp(-SRdiffs[1], SRdifftable, multipliers)
57 |
58 | new_risk_weights=[risk_weights[0]*multiplier_equity, risk_weights[1]*multipliers_bonds]
59 |
60 | if sum(new_risk_weights)>1.0 or not absolute:
61 | ## Normalise...
62 | new_risk_weights=[wt/sum(new_risk_weights) for wt in new_risk_weights]
63 |
64 | return new_risk_weights
65 |
66 | def yield_forecast(data, vols):
67 | eqyield = data.SP_Yield/100.0
68 | bondyield = data.Bond_Yield/100.0
69 |
70 | eqreturn = eqyield / vols[0]
71 | bondreturn = bondyield / vols[1]
72 |
73 | avgreturn = pd.concat([eqreturn, bondreturn], axis=1).mean(axis=1)
74 |
75 | diff = eqreturn - avgreturn
76 |
77 | return diff*3.0
78 |
79 | def yield_forecast_ts(data, vols):
80 | eqyield = data.SP_Yield
81 | bondyield = data.Bond_Yield
82 |
83 | eqreturn = eqyield / vols[0]
84 | bondreturn = bondyield / vols[1]
85 |
86 | eqreturn = eqreturn - pd.rolling_mean(eqreturn, 240, min_periods=1)
87 | bondreturn = bondreturn - pd.rolling_mean(bondreturn, 240, min_periods=1)
88 |
89 |
90 | diff = eqreturn - bondreturn
91 |
92 | return diff*3.0
93 |
94 |
95 |
96 | def trailing_forecast( data, vols):
97 |
98 | eqreturn=(data.SP_TR_Index / data.SP_TR_Index.shift(12))-1
99 | bondreturn=(data.Bond_TR_Index / data.Bond_TR_Index.shift(12))-1
100 |
101 | eqreturn=eqreturn / vols[0]
102 | bondreturn=bondreturn / vols[1]
103 |
104 |
105 | return [eqreturn*.25, bondreturn*.25]
106 |
107 | def calc_ts_new_risk_weights(data, vols, risk_weights, SRdifftable, multipliers, forecast_func, absolute=False):
108 |
109 | forecasts=forecast_func( data, vols)
110 | forecasts=pd.concat(forecasts,axis=1)
111 | new_weights=[risk_weights_with_model(list(forecasts.irow(frow).values), risk_weights, SRdifftable, multipliers, absolute) for
112 | frow in range(len(forecasts))]
113 |
114 | return pd.DataFrame(new_weights, data.Date, columns=["Equity", "Bond"])
115 |
116 | def calc_ts_fixed_weights(data, risk_weights):
117 | return pd.DataFrame([risk_weights]*len(data.index), data.Date, columns=["Equity", "Bond"])
118 |
119 | def portfolio_return(data, cash_weights):
120 |
121 | asset_returns=data[["SP_TR", "Bond_TR"]]
122 | asset_returns.columns=["Equity", "Bond"]
123 | asset_returns.index=data.Date
124 |
125 | portfolio_returns=asset_returns*cash_weights.shift(1)
126 |
127 | portfolio_returns=portfolio_returns.sum(axis=1)
128 |
129 | return portfolio_returns
130 |
131 | def two_year_fix(weights):
132 | new_index=weights.index[range(0,len(weights.index), 60)]
133 | fix_weights=weights.reindex(new_index.unique())
134 | fix_weights=fix_weights.reindex(weights.index).ffill()
135 |
136 | return fix_weights
137 |
138 | def analyse_returns(prets):
139 | basearithmean = prets.mean()*12
140 | new_std=prets.std()*(12**.5)
141 | variance=new_std**2
142 | gmm=basearithmean- - variance/2.0
143 | gsr=(gmm) / new_std
144 |
145 | return (basearithmean, gmm, new_std, gsr)
146 |
147 |
148 | p1=portfolio_return(data, ts_calc_cash_weights(calc_ts_fixed_weights(data, risk_weights)))
149 | p2=portfolio_return(data, ts_calc_cash_weights(calc_ts_new_risk_weights(data, vols, risk_weights, SRdifftable, multipliers, trailing_forecast, absolute=False)))
150 | p3=portfolio_return(data, ts_calc_cash_weights(calc_ts_new_risk_weights(data, vols, risk_weights, SRdifftable, multipliers, trailing_forecast, absolute=True)))
151 |
152 | #p4=portfolio_return(data, two_year_fix(ts_calc_cash_weights(calc_ts_new_risk_weights(data, vols, risk_weights, SRdifftable, multipliers, yield_forecast))))
153 | #p5=portfolio_return(data, two_year_fix(ts_calc_cash_weights(calc_ts_new_risk_weights(data, vols, risk_weights, SRdifftable, multipliers, yield_forecast_ts))))
154 |
155 | #p1=p1/p1.std()
156 | #p2=p2/p2.std()
157 | #p3=p3/p3.std()
158 | #p4=p4/p4.std()
159 |
160 | p4=p3*p2.std()/p3.std()
161 |
162 | analyse_returns(p1)
163 | analyse_returns(p2)
164 | analyse_returns(p3)
165 | analyse_returns(p4)
166 |
167 | p1=p1.cumsum()
168 | p2=p2.cumsum()
169 | p3=p3.cumsum()
170 | p4=p4.cumsum()
171 |
172 | thing=p4 - p3
173 | thing.plot()
174 | plt.show()
175 |
176 |
177 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
178 | import matplotlib.pyplot as plt
179 | import Image
180 |
181 |
182 |
183 | eqyield = data.SP_Yield/100.0
184 | bondyield = data.Bond_Yield/100.0
185 | eqyield.plot()
186 | bondyield.plot()
187 | show()
188 |
189 |
190 | eqreturn = eqyield / vols[0]
191 | bondreturn = bondyield / vols[1]
192 | eqreturn.index=[x[6:] for x in eqreturn.index]
193 | bondreturn.index=[x[6:] for x in bondreturn.index]
194 |
195 | eqreturn.plot(color="black")
196 | bondreturn.plot(color="gray")
197 | legend( ["Equities", "Bonds"], loc="upper left")
198 | frame=plt.gca()
199 |
200 | #frame.get_yaxis().set_visible(False)
201 |
202 | rcParams.update({'font.size': 18})
203 |
204 | file_process("yieldhistory")
205 |
206 | plt.show()
207 |
208 |
209 |
210 |
211 | diff = eqreturn - bondreturn
212 |
213 | diff.plot()
214 | show()
215 |
216 |
217 | p1.index=[x[6:] for x in p1.index]
218 | p2.index=[x[6:] for x in p2.index]
219 | p3.index=[x[6:] for x in p3.index]
220 | p4.index=[x[6:] for x in p4.index]
221 |
222 |
223 | p1.plot(color="blue", linestyle="-.")
224 | p2.plot(color="gray")
225 | p3.plot(color="black", linestyle="--")
226 | p4.plot(color="black")
227 | legend( ["Fixed weight", "Relative momentum", "Absolute momentum", "Norm absolute momentum"], loc="upper left")
228 | frame=plt.gca()
229 |
230 | frame.get_yaxis().set_visible(False)
231 |
232 | rcParams.update({'font.size': 18})
233 |
234 | file_process("assetallocationmodel")
235 |
236 | plt.show()
237 |
238 |
239 |
240 |
241 | p1.plot(color="black")
242 | p3.plot(color="gray")
243 | legend( ["Fixed weight", "Model weight"], loc="upper left")
244 | frame=plt.gca()
245 |
246 | frame.get_yaxis().set_visible(False)
247 |
248 | rcParams.update({'font.size': 18})
249 |
250 | file_process("assetallocationmodel2")
251 |
252 | plt.show()
253 |
254 |
255 | from copy import copy
256 | from datetime import datetime as dt
257 |
258 | from scipy import stats
259 |
260 | rawdata=copy(data)
261 | tickers=["Stocks", "Bonds"]
262 | rawdata.index=data.Date
263 |
264 |
265 |
266 | def get_forward_tr(tickname, rawdata, months):
267 | asset_returns=data[["SP_TR", "Bond_TR"]]
268 | asset_returns.columns=tickers
269 | asset_returns.index=data.Date
270 |
271 | total_returns=asset_returns.cumsum()[tickname]
272 | return (total_returns.shift(-months) / total_returns) - 1.0
273 |
274 |
275 | vol_ts=pd.DataFrame([vols]*len(rawdata.index), rawdata.index, columns=tickers)
276 | vol_ts.columns=tickers
277 |
278 | alldatatoplot_slope=dict(relative_div=[], relative_ts_div=[], momentum=[])
279 | alldatatoplot_pvalue=dict(relative_div=[], relative_ts_div=[], momentum=[])
280 |
281 | for nmonths in [1,3,6,12,24,36,60,120]:
282 | fwd_return=[get_forward_tr(tickname, rawdata, nmonths) for tickname in tickers]
283 | fwd_return=pd.concat(fwd_return, axis=1)
284 | fwd_return.columns=tickers
285 |
286 | fwd_SR=fwd_return / (vol_ts * ((nmonths/12.0)**.5))
287 | rel_SR=fwd_SR.Stocks - fwd_SR.Bonds
288 |
289 | stacked_SR=rel_SR.values
290 |
291 | predictors=[yield_forecast(data, vols).shift(1),
292 | yield_forecast_ts(data, vols).shift(1),
293 | trailing_forecast(data, vols).shift(1)]
294 |
295 | for (predname, predset) in zip(["relative_div","relative_ts_div","momentum"], predictors):
296 | stack_pred=list(predset.values)
297 | valididx=[~np.isnan(stacked_SR[idx]) & ~np.isnan(stack_pred[idx]) for idx in range(len(stacked_SR))]
298 | stack_pred_valid=[stack_pred[idx] for idx in range(len(stack_pred)) if valididx[idx]]
299 | stack_SR_valid=[stacked_SR[idx] for idx in range(len(stacked_SR)) if valididx[idx]]
300 |
301 | slope, intercept, r_value, p_value, std_err=stats.linregress(stack_pred_valid, stack_SR_valid)
302 |
303 | print("%d months ahead, predictor %s, slope %.3f p_value %.4f R2 %.4f" % (
304 | nmonths, predname, slope, p_value, r_value))
305 |
306 | alldatatoplot_slope[predname].append(slope)
307 | alldatatoplot_pvalue[predname].append((1.0 - p_value)*sign(slope))
308 |
309 | from matplotlib.pyplot import show, plot, legend, title
310 |
311 | plot([1,3,6,12,24,36,60,120], alldatatoplot_slope["momentum"])
312 | plot([1,3,6,12,24,36,60,120], alldatatoplot_slope["relative_div"])
313 | plot([1,3,6,12,24,36,60,120], alldatatoplot_slope["relative_ts_div"])
314 | legend(["momentum", "relative_div", "relative_ts_div"])
315 |
316 | show()
317 |
318 | plot([1,3,6,12,24,36,60,120], alldatatoplot_pvalue["momentum"])
319 | plot([1,3,6,12,24,36,60,120], alldatatoplot_pvalue["relative_div"])
320 | plot([1,3,6,12,24,36,60,120], alldatatoplot_pvalue["relative_ts_div"])
321 | legend(["momentum", "relative_div", "relative_ts_div"])
322 |
323 | show()
324 |
--------------------------------------------------------------------------------
/plots_for_perhaps/basicopt.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | import Image
4 | from random import gauss
5 | import numpy as np
6 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, scatter
7 |
8 | import matplotlib.pylab as plt
9 | from itertools import cycle
10 | import pickle
11 | import pandas as pd
12 |
13 | lines = ["--","-","-."]
14 | linecycler = cycle(lines)
15 |
16 | from optimisation import *
17 |
18 | def creturn(weights, mus):
19 | return (np.matrix(weights)*mus)[0,0]
20 |
21 | def csd(weights, sigma):
22 | return (variance(weights,sigma)**.5)
23 |
24 | def cgm(weights, mus, std_dev):
25 | estreturn=creturn(weights, mus)
26 |
27 | return estreturn - 0.5*(std_dev**2)
28 |
29 |
30 | def geo_SR(weights, sigma, mus):
31 | ## Returns minus the Sharpe Ratio (as we're minimising)
32 |
33 | """
34 | estreturn=250.0*((np.matrix(x)*mus)[0,0])
35 | variance=(variance(x,sigma)**.5)*16.0
36 | """
37 | std_dev=csd(weights, sigma)
38 | geomean = cgm(weights, mus, std_dev)
39 |
40 | return -geomean/std_dev
41 |
42 | def arith_SR(weights, sigma, mus):
43 | ## Returns minus the Sharpe Ratio (as we're minimising)
44 |
45 | """
46 | estreturn=250.0*((np.matrix(x)*mus)[0,0])
47 | variance=(variance(x,sigma)**.5)*16.0
48 | """
49 | std_dev=csd(weights, sigma)
50 | amean = creturn(weights, mus)
51 |
52 | return -amean/std_dev
53 |
54 | def basic_opt(std,corr,mus):
55 | number_assets=mus.shape[0]
56 | sigma=sigma_from_corr(std, corr)
57 | start_weights=[1.0/number_assets]*number_assets
58 |
59 | ## Constraints - positive weights, adding to 1.0
60 | bounds=[(0.0,1.0)]*number_assets
61 | cdict=[{'type':'eq', 'fun':addem}]
62 |
63 | return minimize(geo_SR, start_weights, (sigma, mus), method='SLSQP', bounds=bounds, constraints=cdict, tol=0.00001)
64 |
65 |
66 | mus=np.array([[0.04,0.04, .0625]]).transpose()
67 | std=np.array([[0.08,0.08,.12]]).transpose()
68 | #mus=np.array([[0.0186,0.0827]]).transpose()
69 | #std=np.array([[0.0623,0.198]]).transpose()
70 |
71 | c2=0.9
72 | c1=.9
73 | c3=0.9
74 | corr=np.array([[1,c1,c2],[c1,1.0,c3],[c2,c3,1.0]])
75 |
76 | print basic_opt(std,corr,mus)
77 |
78 |
79 |
80 | mus=np.array([[0.016, 0.05]]).transpose()
81 | std=np.array([[0.0827,0.198]]).transpose()
82 |
83 | c1=.05
84 | corr=np.array([[1,c1],[c1,1.0]])
85 |
86 | def wtfunc(bondweight):
87 | return [bondweight, 1.0 - bondweight]
88 |
89 | rets=[]
90 | sds=[]
91 | gms=[]
92 | gsr=[]
93 | asr=[]
94 |
95 | bondwts=np.arange(0.0, 1.0, 0.01)
96 |
97 | for bondweight in bondwts:
98 | weights=wtfunc(bondweight)
99 | sigma=sigma_from_corr(std, corr)
100 | std_dev=csd(weights, sigma)
101 |
102 | sds.append(std_dev)
103 | rets.append(creturn(weights, mus))
104 | gms.append(cgm(weights, mus, std_dev))
105 |
106 | gsr.append(-geo_SR(weights, sigma, mus))
107 | asr.append(-arith_SR(weights, sigma, mus))
108 |
109 |
110 | def file_process(filename):
111 | fig = plt.gcf()
112 | fig.set_size_inches(18.5,10.5)
113 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
114 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
115 |
116 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
117 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
118 |
119 |
120 |
121 |
122 | plot(bondwts, gms)
123 | frame=plt.gca()
124 |
125 | #frame.set_xticks([0.6, 0.7, 0.8, 0.9])
126 | frame.set_ylim([0.01, 0.04])
127 | frame.set_yticks([0.01, 0.02, 0.03])
128 | rcParams.update({'font.size': 18})
129 | file_process("wts_rets_new")
130 |
131 | plt.show()
132 |
133 | plot(bondwts, sds)
134 | frame=plt.gca()
135 |
136 | frame.set_ylim([0.05, 0.2])
137 | frame.set_yticks([0.05, 0.1, 0.15, 0.2])
138 |
139 | rcParams.update({'font.size': 18})
140 | file_process("wts_sds")
141 |
142 | plt.show()
143 |
144 | plot(bondwts, gsr)
145 | frame=plt.gca()
146 | frame.set_ylim([0.2, 0.5])
147 | frame.set_yticks([0.2, 0.3, 0.4, 0.5])
148 |
149 | rcParams.update({'font.size': 18})
150 | file_process("wts_sr")
151 |
152 | plt.show()
153 |
154 | print basic_opt(std,corr,mus)
--------------------------------------------------------------------------------
/plots_for_perhaps/birdinthehand.py:
--------------------------------------------------------------------------------
1 | import Image
2 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, scatter
3 |
4 | import matplotlib.pyplot as plt
5 | import numpy as np
6 |
7 | cost=0.012 - 0.005
8 | stdev=0.302 - 0.289
9 |
10 | ## generate guassian points from benefit
11 | volstructure=[ 1.645*stdev, 1.28*stdev, 0.6666*stdev]
12 | midbenefit=0.302 - 0.25
13 |
14 | x=[0.0, 1.0]
15 |
16 | ones = np.ones(2)
17 |
18 | fig, ax = plt.subplots()
19 |
20 | for i, val in enumerate(volstructure):
21 | alpha = 0.5*(i+1)/len(volstructure) # Modify the alpha value for each iteration.
22 | ax.fill_between(x, [-cost, midbenefit-cost+val], [-cost, midbenefit-cost-val], color='red', alpha=alpha)
23 |
24 | ax.plot(x, [-cost, midbenefit-cost], color='black', linewidth=3) # Plot the original signal
25 |
26 | ax.set_ylim([-cost-np.max(volstructure), midbenefit-cost+np.max(volstructure)+stdev/2.0])
27 | ax.set_yticks([ -cost, 0.0, -cost+midbenefit])
28 | ax.get_xaxis().set_visible(False)
29 | plt.axhline(0.0, linestyle="--")
30 |
31 | def file_process(filename):
32 | fig = plt.gcf()
33 | fig.set_size_inches(18.5,10.5)
34 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
35 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
36 |
37 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
38 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
39 |
40 | rcParams.update({'font.size': 18})
41 |
42 | file_process("birdinthehanddivbenefit")
43 |
44 | plt.show()
45 |
46 |
--------------------------------------------------------------------------------
/plots_for_perhaps/conditionalforecast.py:
--------------------------------------------------------------------------------
1 | """
2 | test the 12 month asset allocation model
3 |
4 | no costs currently
5 | """
6 |
7 | import pandas as pd
8 | import numpy as np
9 | from datetime import datetime as dt
10 |
11 | def file_process(filename):
12 | fig = plt.gcf()
13 | fig.set_size_inches(18.5,10.5)
14 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
15 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
16 |
17 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
18 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
19 |
20 | """
21 | data=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/USmonthlyreturns.csv")
22 | data.index=data.Date
23 |
24 | vols=[0.15, 0.08]
25 |
26 | risk_weights=[.5, .5]
27 |
28 | """
29 |
30 | SRdifftable=[ -0.25, -0.2, -0.15, -0.1, -0.05, 0.0, 0.05, 0.1, 0.15, 0.2, 0.25]
31 | multipliers=[.6, .66, .77, .85, .94, 1.0, 1.11, 1.19, 1.3, 1.37, 1.48]
32 |
33 |
34 | def read_ts_csv(fname, dindex="Date"):
35 | data=pd.read_csv(fname)
36 | dateindex=[dt.strptime(dx, "%d/%m/%y") for dx in list(data[dindex])]
37 | data.index=dateindex
38 | del(data[dindex])
39 |
40 | return data
41 |
42 |
43 | rawdata=read_ts_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/MSCI_data.csv")
44 | refdata=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/MSCI_ref.csv")
45 |
46 | def sign(x):
47 | if x is None:
48 | return None
49 | if np.isnan(x):
50 | return np.NAN
51 | if x==0:
52 | return 0.0
53 | if x<0:
54 | return -1.0
55 | if x>0:
56 | return 1.0
57 |
58 |
59 |
60 | def get_annual_tr(tickname, rawdata):
61 | total_returns=rawdata[tickname+"_TR"]
62 | pecrets=(total_returns / total_returns.shift(12)) - 1.0
63 | mperces=(total_returns.diff()/total_returns.shift(1)) - 1.0
64 | stdrets=mperces.std()*(12**.5)
65 | return pecrets/stdrets
66 |
67 | def get_forward_tr(tickname, rawdata, months):
68 | total_returns=rawdata[tickname+"_TR"]
69 | pecrets=(total_returns.shift(-months) / total_returns) - 1.0
70 | mperces=(total_returns.diff()/total_returns.shift(1)) - 1.0
71 | stdrets=mperces.std()*(12**.5)
72 | return pecrets/stdrets
73 |
74 |
75 |
76 | tickers=list(refdata[refdata.Type=="Country"].Country.values) #mom 17bp
77 |
78 |
79 | def vl(emordev):
80 | if emordev=="EM":
81 | return .23
82 | else:
83 | return .15
84 |
85 | vols=[vl(refdata[refdata.Country==ticker].EmorDEV.values[0]) for ticker in tickers]
86 |
87 | def get_monthly_tr(tickname, rawdata):
88 | total_returns=rawdata[tickname+"_TR"]
89 | return (total_returns / total_returns.shift(1)) - 1.0
90 |
91 | def calc_asset_returns(rawdata, tickers):
92 | asset_returns=pd.concat([get_monthly_tr(tickname, rawdata) for tickname in tickers], axis=1)
93 | asset_returns.columns=tickers
94 |
95 | return asset_returns
96 |
97 | data=calc_asset_returns(rawdata, tickers)
98 |
99 | conditioner=[get_annual_tr(tickner, rawdata) for tickner in tickers]
100 | dependent=[get_forward_tr(tickner, rawdata, 3) for tickner in tickers]
101 |
102 | ## OR ...
103 | #import random
104 | #conditioner=[pd.Series([random.gauss(0.0, 1.0) for unused in ehup.index], ehup.index) for ehup in conditioner]
105 |
106 |
107 |
108 |
109 |
110 | futreturns=[list(x.values) for x in dependent]
111 | condvariable=[list(x.values) for x in conditioner]
112 |
113 | futreturns=sum(futreturns, [])
114 | condvariable=sum(condvariable, [])
115 |
116 |
117 | cmean=np.nanmean(condvariable)
118 | cstd=np.nanstd(condvariable)
119 |
120 | condreturns_pair=[(fr,cv) for fr,cv in zip(futreturns, condvariable) if not np.isnan(fr) and not np.isnan(cv)]
121 |
122 | upper_bound1=cmean+4*cstd
123 | lower_bound1=cmean+cstd
124 | condreturns=[fr for fr,cv in condreturns_pair if cvlower_bound1]
125 | condreturns_cond=[cv for fr,cv in condreturns_pair if cv<=upper_bound1 and cv>lower_bound1]
126 |
127 |
128 | upper_bound2=cmean+cstd
129 | lower_bound2=cmean-cstd
130 | condreturns2=[fr for fr,cv in condreturns_pair if cvlower_bound2]
131 | condreturns_cond2=[cv for fr,cv in condreturns_pair if cv<=upper_bound2 and cv>lower_bound2]
132 |
133 |
134 |
135 | upper_bound3=cmean-cstd
136 | lower_bound3=cmean-4*cstd
137 | condreturns3=[fr for fr,cv in condreturns_pair if cvlower_bound3]
138 | condreturns_cond3=[cv for fr,cv in condreturns_pair if cv<=upper_bound3 and cv>lower_bound3]
139 |
140 | def thing(retstrip):
141 | avg=np.nanmean(retstrip)
142 | std=np.nanstd(retstrip)
143 | length=len([x for x in retstrip if not np.isnan(x)])
144 | return (avg, std, length, std/(length**.5))
145 |
146 | from optimisation import *
147 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
148 | import matplotlib.pyplot as plt
149 | import Image
150 |
151 | from itertools import cycle
152 |
153 | def linehist(x, color="blue", linestyle="-"):
154 | y,binEdges =np.histogram(x, bins=50)
155 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
156 | plot(bincenters,y,'-', color=color, linestyle=linestyle)
157 |
158 |
159 | lines = ["-","--"]
160 | linecycler = cycle(lines)
161 | colorcycler=cycle(["red", "blue"])
162 |
163 | linehist(condreturns, linestyle=next(linecycler), color=next(colorcycler))
164 | linehist(condreturns2, linestyle=next(linecycler), color=next(colorcycler))
165 |
166 |
167 |
168 |
169 | frame=plt.gca()
170 |
171 | frame.get_yaxis().set_visible(False)
172 | frame.set_xticks([-1.0, 0, 1.0])
173 | frame.set_xlim([-2.0, 2.0])
174 |
175 | legend(["Above %.2f" % lower_bound1,"Below %.2f" % upper_bound2])
176 |
177 | rcParams.update({'font.size': 18})
178 |
179 |
180 | file_process("conditionalreturn")
181 |
182 |
183 |
184 | show()
185 |
186 |
--------------------------------------------------------------------------------
/plots_for_perhaps/correlatedreturns.py:
--------------------------------------------------------------------------------
1 |
2 | import Image
3 | from random import gauss
4 | import numpy as np
5 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, scatter
6 |
7 | import matplotlib.pylab as plt
8 | from itertools import cycle
9 | import pickle
10 | import pandas as pd
11 |
12 | lines = ["--","-","-."]
13 | linecycler = cycle(lines)
14 |
15 | def twocorrelatedseries(no_periods, period_mean, period_mean2, period_vol, corr):
16 |
17 | means = [period_mean, period_mean2]
18 | stds = [period_vol]*2
19 | covs = [[stds[0]**2 , stds[0]*stds[1]*corr],
20 | [stds[0]*stds[1]*corr, stds[1]**2]]
21 |
22 | m = np.random.multivariate_normal(means, covs, no_periods).T
23 |
24 | data1=m[0]
25 | data2=m[1]
26 |
27 | return np.mean(data1) - np.mean(data2)
28 |
29 |
30 |
31 |
32 |
33 | ## path to difference for one thing no correlation
34 | months_in_year=12
35 | annual_vol=0.150
36 | monthly_vol=annual_vol/(months_in_year**.5)
37 | annual_SR=0.66
38 | annual_SR2=0.46
39 | diffSR=annual_SR - annual_SR2
40 | annual_return=annual_vol*annual_SR
41 | annual_return2=annual_vol*annual_SR2
42 | monthly_mean=annual_return/months_in_year
43 | monthly_mean2=annual_return2/months_in_year
44 |
45 | ## Make sure these match!
46 | no_years=10
47 | no_periods=months_in_year*no_years
48 |
49 | monte_carlos=500000
50 | corr=0.85
51 |
52 | rollmeandiff=[twocorrelatedseries(no_periods, monthly_mean, monthly_mean2, monthly_vol, corr=corr) for ii in range(monte_carlos)]
53 | rollanndiff=[x*12 for x in rollmeandiff]
54 |
55 | rollannSR=[x/annual_vol for x in rollanndiff]
56 |
57 |
58 | def linehist(x, color="blue", linestyle="-", bins=10, linewidth=1):
59 | y,binEdges =np.histogram(x, bins=bins)
60 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
61 | plot(bincenters,y,'-', color=color, linestyle=linestyle, linewidth=linewidth)
62 |
63 | linehist(rollanndiff, bins=50, linewidth=2)
64 | frame=plt.gca()
65 |
66 | frame.get_yaxis().set_visible(False)
67 | frame.set_xlim([-0.07, 0.13])
68 | frame.set_xticks([-0.05, 0.00, 0.05,.1])
69 | frame.set_ylim([0,50000])
70 |
71 |
72 | frame.annotate("Expected improvement in returns", xy=(0.03, 38000),xytext=(0.05, 45000.0), arrowprops=dict(facecolor='black', shrink=0.05), size=18)
73 | frame.annotate("Breakeven in costs", xy=(0.01, 28000),xytext=(-0.05, 40000.0), arrowprops=dict(facecolor='black', shrink=0.05), size=18)
74 |
75 |
76 | plt.axvline(0.01, linestyle="--")
77 | plt.axvline(0.03, linestyle="--")
78 |
79 |
80 | #xlabel("Difference in annual % returns between managers")
81 |
82 | rcParams.update({'font.size': 18})
83 |
84 |
85 |
86 | def file_process(filename):
87 | fig = plt.gcf()
88 | fig.set_size_inches(18.5,10.5)
89 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
90 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
91 |
92 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
93 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
94 |
95 |
96 | file_process("correlateddifferences")
97 |
98 | show()
99 |
100 | print(sum([1.0 for x in rollanndiff if x<0.01])/len(rollanndiff))
101 | print(np.std(rollanndiff))
--------------------------------------------------------------------------------
/plots_for_perhaps/costbreakevenplots.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
4 | import Image
5 | import pandas as pd
6 |
7 | def file_process(filename):
8 | fig = plt.gcf()
9 | fig.set_size_inches(18.5,10.5)
10 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
11 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
12 |
13 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
14 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
15 |
16 | costsnotrading=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/etfvssharesbreakeven.csv")
17 |
18 | costsnotrading.index=costsnotrading.Value
19 | costsnotrading=costsnotrading.drop("Value", 1)
20 |
21 | costsnotrading.plot(style=['b-','g--.'])
22 |
23 | frame=plt.gca()
24 | frame.set_ylim([0.0, 0.3])
25 | frame.set_yticks([ 0.1, 0.2, 0.3])
26 |
27 | frame.set_xticks([10000, 15000, 20000])
28 |
29 |
30 |
31 | rcParams.update({'font.size': 18})
32 | file_process("etfsharesbreakeven")
33 |
34 | show()
35 |
36 |
37 | costsnotrading=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/etfvssharesbreakevenwithtrading.csv")
38 |
39 | costsnotrading.index=costsnotrading.Value
40 | costsnotrading=costsnotrading.drop("Value", 1)
41 |
42 | costsnotrading.plot(style=['b-','g--.'])
43 |
44 | frame=plt.gca()
45 | frame.set_ylim([0.0, 0.6])
46 | frame.set_yticks([ 0.2, 0.4, 0.6])
47 |
48 | frame.set_xticks([100000, 200000, 300000, 400000, 500000])
49 |
50 |
51 |
52 | rcParams.update({'font.size': 18})
53 | file_process("etfsharesbreakevenwithtrading")
54 |
55 | show()
56 |
57 |
--------------------------------------------------------------------------------
/plots_for_perhaps/costplots.py:
--------------------------------------------------------------------------------
1 | from optimisation import *
2 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
3 | import matplotlib.pyplot as plt
4 | import Image
5 | import pandas as pd
6 |
7 | from itertools import cycle, islice
8 |
9 | lines = ["-","--","-."]
10 |
11 | filename="/home/rob/stuff/Writing/NonFiction/Quant/perhaps/pictures/tradingCostsDirectIndirect.csv"
12 |
13 | results=pd.read_csv(filename)
14 | results.columns=[""]+list(results.columns[1:])
15 | results.index=results.iloc[:,0]
16 |
17 | linecycler = cycle(lines)
18 | colorcycler=cycle(["red", "blue", "green"])
19 |
20 |
21 | plt.subplot(2,1,1)
22 | toplot=results["FIXED"]
23 | toplot.plot(kind="bar")
24 | ylabel("Fixed cost % per year")
25 |
26 | plt.grid(False)
27 | frame=plt.gca()
28 | frame.set_ylim([-0.1, 1.5])
29 | frame.set_yticks([ 0.0, 0.5, 1.0])
30 | frame.get_xaxis().set_ticks_position('none')
31 | #for tick in frame.get_xaxis().get_majorticklabels():
32 | # tick.set_horizontalalignment("center")
33 | frame.set_xticklabels([])
34 |
35 |
36 | linecycler = cycle(lines)
37 | colorcycler=cycle(["red", "blue", "green"])
38 |
39 | plt.subplot(2,1,2)
40 | toplot=results.CPT
41 | ans=toplot.plot(kind="bar")
42 | ylabel("Cost per turnover %")
43 | plt.grid(False)
44 | frame=plt.gca()
45 |
46 | frame.set_ylim([-0.1, 1.5])
47 | frame.set_yticks([ 0.0, 0.5, 1.0])
48 | frame.get_xaxis().set_ticks_position('none')
49 | frame.set_xticklabels([])
50 | plt.text(0.4, -0.4, results.index[0])
51 | plt.text(1.4, -0.4, results.index[1])
52 | plt.text(2.5, -0.4, results.index[2])
53 |
54 | #for tick in frame.get_xaxis().get_majorticklabels():
55 | # tick.set_horizontalalignment("center")
56 |
57 |
58 | #frame.get_xaxis().set_visible(False)
59 | #frame.get_yaxis().set_visible(False)
60 |
61 | #frame.set_xticks([-0.9, 0.0, 0.9])
62 |
63 | rcParams.update({'font.size': 18})
64 |
65 |
66 |
67 | def file_process(filename):
68 | fig = plt.gcf()
69 | fig.set_size_inches(18.5,10.5)
70 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
71 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
72 |
73 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
74 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
75 |
76 | file_process("costsdirectindirect")
77 |
78 |
79 | show()
80 |
81 | filename="/home/rob/stuff/Writing/NonFiction/Quant/perhaps/pictures/tradingCostsSize.csv"
82 |
83 | results=pd.read_csv(filename)
84 | results.columns=[""]+list(results.columns[1:])
85 | results.index=results.iloc[:,0]
86 |
87 | linecycler = cycle(lines)
88 | colorcycler=cycle(["red", "blue", "green"])
89 |
90 |
91 | plt.subplot(2,1,1)
92 | toplot=results[results.Country=="US"].FIXED
93 | toplot.plot(linestyle=next(linecycler), color=next(colorcycler), linewidth=3)
94 | toplot=results[results.Country=="UK"].FIXED
95 | toplot.plot(linestyle=next(linecycler), color=next(colorcycler), linewidth=3)
96 |
97 | ylabel("Fixed cost % per year")
98 |
99 | plt.legend(["US", "UK"],loc="upper right")
100 |
101 | plt.grid(False)
102 | frame=plt.gca()
103 | frame.set_ylim([-0.01, 0.075])
104 | frame.set_yticks([ 0.0, 0.03, 0.06])
105 | frame.get_xaxis().set_ticks_position('none')
106 | current_xlim=frame.get_xlim()
107 | frame.set_xlim([current_xlim[0]-0.1, current_xlim[1]+.1])
108 | #for tick in frame.get_xaxis().get_majorticklabels():
109 | # tick.set_horizontalalignment("center")
110 | frame.set_xticklabels([])
111 |
112 |
113 | linecycler = cycle(lines)
114 | colorcycler=cycle(["red", "blue", "green"])
115 |
116 | plt.subplot(2,1,2)
117 | toplot=results[results.Country=="US"].CPT
118 | toplot.plot(linestyle=next(linecycler), color=next(colorcycler), linewidth=3)
119 | toplot=results[results.Country=="UK"].CPT
120 | toplot.plot(linestyle=next(linecycler), color=next(colorcycler), linewidth=3)
121 | ylabel("Cost per turnover %")
122 | plt.grid(False)
123 | frame=plt.gca()
124 |
125 | frame.set_ylim([-0.1, 1.5])
126 | frame.set_yticks([ 0.0, 0.5, 1.0])
127 | frame.get_xaxis().set_ticks_position('none')
128 | current_xlim=frame.get_xlim()
129 | frame.set_xlim([current_xlim[0]-0.1, current_xlim[1]+0.1])
130 | frame.set_xticklabels([])
131 | plt.text(-0.1, -0.4, results.index[0])
132 | plt.text(0.9, -0.4, results.index[1])
133 | plt.text(1.9, -0.4, results.index[2])
134 | plt.text(2.9, -0.4, results.index[3])
135 | plt.text(3.9, -0.4, results.index[4])
136 |
137 |
138 | #for tick in frame.get_xaxis().get_majorticklabels():
139 | # tick.set_horizontalalignment("center")
140 |
141 |
142 | #frame.get_xaxis().set_visible(False)
143 | #frame.get_yaxis().set_visible(False)
144 |
145 | #frame.set_xticks([-0.9, 0.0, 0.9])
146 |
147 | rcParams.update({'font.size': 18})
148 |
149 | file_process("costsbysize")
150 |
151 | show()
152 |
153 |
154 | filename="/home/rob/stuff/Writing/NonFiction/Quant/perhaps/pictures/tradingCostsByCountry_FIXED.csv"
155 |
156 | results1=pd.read_csv(filename)
157 | results1.columns=[""]+list(results1.columns[1:])
158 | results1.index=results1.iloc[:,0]
159 |
160 | filename="/home/rob/stuff/Writing/NonFiction/Quant/perhaps/pictures/tradingCostsByCountry_CPT.csv"
161 |
162 | results2=pd.read_csv(filename)
163 | results2.columns=[""]+list(results2.columns[1:])
164 | results2.index=results2.iloc[:,0]
165 |
166 |
167 | my_colors = list(islice(cycle(['b', 'r', 'k']), None, len(results2)))
168 | ax=results1.plot(kind="bar", color=my_colors)
169 | ax.set_xticklabels(["UK", "German", "US"], rotation=0)
170 |
171 | ylabel("Fixed cost % per year")
172 |
173 |
174 | plt.grid(False)
175 |
176 | rcParams.update({'font.size': 18})
177 |
178 | file_process("costsbycountryFIXED")
179 |
180 | show()
181 |
182 |
183 | ax=results2.plot(kind="bar", color=my_colors)
184 |
185 |
186 | ylabel("Cost per turnover %")
187 | ax.set_xticklabels(["UK", "German", "US"], rotation=0)
188 | plt.grid(False)
189 |
190 |
191 |
192 |
193 | rcParams.update({'font.size': 18})
194 |
195 | file_process("costsbycountryCPT")
196 |
197 | show()
198 |
199 |
200 | filename="/home/rob/stuff/Writing/NonFiction/Quant/perhaps/pictures/tradingCostsMarketCapFIXED.csv"
201 |
202 | results1=pd.read_csv(filename)
203 | results1.columns=[""]+list(results1.columns[1:])
204 | results1.index=results1.iloc[:,0]
205 |
206 | filename="/home/rob/stuff/Writing/NonFiction/Quant/perhaps/pictures/tradingCostsMarketCapCPT.csv"
207 |
208 | results2=pd.read_csv(filename)
209 | results2.columns=[""]+list(results2.columns[1:])
210 | results2.index=results2.iloc[:,0]
211 |
212 |
213 | my_colors = list(islice(cycle(['b', 'r', 'k']), None, len(results2)))
214 | ax=results1.plot(kind="bar", color=my_colors)
215 | ax.set_xticklabels(["Large cap", "Mid cap", "Small cap"], rotation=0)
216 |
217 | ylabel("Fixed cost % per year")
218 |
219 |
220 | plt.grid(False)
221 |
222 | rcParams.update({'font.size': 18})
223 |
224 | file_process("costsbycapFIXED")
225 |
226 | show()
227 |
228 |
229 | ax=results2.plot(kind="bar", color=my_colors)
230 |
231 |
232 | ylabel("Cost per turnover %")
233 | ax.set_xticklabels(["Large cap", "Mid cap", "Small cap"], rotation=0)
234 | plt.grid(False)
235 |
236 |
237 |
238 |
239 | rcParams.update({'font.size': 18})
240 |
241 | file_process("costsbycapCPT")
242 |
243 | show()
244 |
245 |
--------------------------------------------------------------------------------
/plots_for_perhaps/costs.py:
--------------------------------------------------------------------------------
1 |
2 | from optimisation import *
3 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
4 | import matplotlib.pyplot as plt
5 | import Image
6 |
7 | from itertools import cycle
8 |
9 | from datetime import datetime as dt
10 |
11 | lines = ["-","--","-.", ]
12 | linecycler = cycle(lines)
13 | colorcycler=cycle(["red", "blue"])
14 |
15 | from random import gauss
16 | import numpy as np
17 | import pandas as pd
18 |
19 | mean=0.05
20 | costladder=[0.0, 0.0001, 0.001, 0.005, 0.01]
21 |
22 | nlength=2016 - 1249
23 |
24 | results=[]
25 | for cost in costladder:
26 | x=[1+(mean - cost)]*nlength
27 | x=np.cumprod(x)
28 |
29 | x=x*100.0
30 | x=pd.Series(x, pd.date_range(pd.datetime(2016,1,1), periods=nlength, freq="D"))
31 |
32 | results.append(x)
33 |
34 | results=pd.concat(results, axis=1)
35 | rel_labels=["%d bp" % (cost*10000.0) for cost in costladder]
36 | results.columns = rel_labels
37 |
38 | for costlabel in rel_labels:
39 | results[costlabel].plot(linestyle=next(linecycler), color=next(colorcycler))
40 | legend(loc="upper left")
41 |
42 | frame=plt.gca()
43 |
44 | #frame.get_xaxis().set_visible(False)
45 | frame.set_yticks([0.0, 1000.0, 2000.0])
46 |
47 |
48 |
49 |
50 | rcParams.update({'font.size': 18})
51 |
52 | def file_process(filename):
53 | fig = plt.gcf()
54 | fig.set_size_inches(18.5,10.5)
55 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
56 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
57 |
58 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
59 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
60 |
61 | file_process("costovertime")
62 |
63 | show()
64 |
65 |
--------------------------------------------------------------------------------
/plots_for_perhaps/devcapweights.csv:
--------------------------------------------------------------------------------
1 | Country,Weight
2 | UK,0.0722222222
3 | FRANCE,0.0366666667
4 | GERMANY,0.0344444444
5 | SWITZERLAND,0.0344444444
6 | SPAIN,0.0122222222
7 | NETHERLANDS,0.0111111111
8 | SWEDEN,0.0111111111
9 | ITALY,0.0077777778
10 | DENMARK,0.0077777778
11 | BELGIUM,0.0055555556
12 | FINLAND,0.0033333333
13 | ISRAEL,0.0022222222
14 | NORWAY,0.0022222222
15 | IRELAND,0.0022222222
16 | AUSTRIA,0.0011111111
17 | PORTUGAL,0.0011111111
18 | JAPAN,0.0833333333
19 | AUSTRALIA,0.0266666667
20 | HONG_KONG,0.0122222222
21 | SINGAPORE,0.0055555556
22 | NEW_ZEALAND,0.0011111111
23 | USA,0.5911111111
24 | CANADA,0.0344444444
25 |
--------------------------------------------------------------------------------
/plots_for_perhaps/devhcweights.csv:
--------------------------------------------------------------------------------
1 | Country,Weight
2 | AUSTRALIA,0.069999993
3 | HONG_KONG,0.049999995
4 | JAPAN,0.13333332
5 | NEW_ZEALAND,0.029999997
6 | SINGAPORE,0.049999995
7 | AUSTRIA,0.006666666
8 | BELGIUM,0.013333332
9 | FINLAND,0.013333332
10 | FRANCE,0.046666662
11 | GERMANY,0.029999997
12 | IRELAND,0.006666666
13 | ISRAEL,0.013333332
14 | ITALY,0.026666664
15 | NETHERLANDS,0.013333332
16 | NORWAY,0.013333332
17 | PORTUGAL,0.009999999
18 | SPAIN,0.036666663
19 | SWEDEN,0.013333332
20 | SWITZERLAND,0.029999997
21 | UK,0.059999994
22 | USA,0.19999998
23 | CANADA,0.13333332
24 |
--------------------------------------------------------------------------------
/plots_for_perhaps/divbenefits.py:
--------------------------------------------------------------------------------
1 |
2 | import Image
3 | from random import gauss
4 | import numpy as np
5 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, scatter
6 |
7 | import matplotlib.pylab as plt
8 | from itertools import cycle
9 | import pickle
10 | import pandas as pd
11 |
12 | lines = ["--","-","-."]
13 | linecycler = cycle(lines)
14 |
15 | def twocorrelatedseries(no_periods, period_mean, period_mean2, period_vol, corr):
16 |
17 | means = [period_mean, period_mean2]
18 | stds = [period_vol]*2
19 | covs = [[stds[0]**2 , stds[0]*stds[1]*corr],
20 | [stds[0]*stds[1]*corr, stds[1]**2]]
21 |
22 | m = np.random.multivariate_normal(means, covs, no_periods).T
23 |
24 | data1=m[0]
25 | data2=m[1]
26 |
27 | empirical_corr=np.corrcoef(data1, data2)[0][1]
28 |
29 |
30 |
31 | return empirical_corr
32 |
33 |
34 |
35 |
36 |
37 | ## path to difference for one thing no correlation
38 | months_in_year=12
39 | annual_vol=0.270
40 | monthly_vol=annual_vol/(months_in_year**.5)
41 | annual_SR=0.05
42 | annual_SR2=0.05
43 | diffSR=annual_SR - annual_SR2
44 | annual_return=annual_vol*annual_SR
45 | annual_return2=annual_vol*annual_SR2
46 | monthly_mean=annual_return/months_in_year
47 | monthly_mean2=annual_return2/months_in_year
48 |
49 | ## Make sure these match!
50 | no_periods=36
51 |
52 | monte_carlos=1000000
53 | corr=0.75
54 |
55 | corrdist=[twocorrelatedseries(no_periods, monthly_mean, monthly_mean2, monthly_vol, corr=corr) for ii in range(monte_carlos)]
56 |
57 | print np.percentile(corrdist, 75)
58 | print np.percentile(corrdist, 50)
59 | print np.mean(corrdist)
60 |
61 | def linehist(x, color="blue", linestyle="-", bins=10, linewidth=1):
62 | y,binEdges =np.histogram(x, bins=bins)
63 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
64 | plot(bincenters,y,'-', color=color, linestyle=linestyle, linewidth=linewidth)
65 |
66 | linehist(corrdist, bins=50, linewidth=2)
67 |
68 |
69 | frame=plt.gca()
70 |
71 | frame.get_yaxis().set_visible(False)
72 | frame.set_xlim([0.5, 0.95])
73 | frame.set_xticks([0.6, 0.7, 0.8, 0.9])
74 | frame.set_ylim([0,100000])
75 |
76 |
77 |
78 | frame.annotate("Average correlation", xy=(0.745, 85000),xytext=(0.6, 80000.0), arrowprops=dict(facecolor='black', shrink=0.05), size=18)
79 | frame.annotate("75% confidence\n correlation", xy=(0.8, 83000),xytext=(0.84, 80000.0), arrowprops=dict(facecolor='black', shrink=0.05), size=18)
80 | frame.annotate("Breakeven \n with costs", xy=(0.92, 500),xytext=(0.855, 40000.0), arrowprops=dict(facecolor='black', shrink=0.05), size=18)
81 |
82 |
83 |
84 | plt.axvline(0.75, linestyle="--")
85 | plt.axvline(0.8, linestyle="--")
86 | plt.axvline(0.92, linestyle="--")
87 |
88 |
89 | #xlabel("Difference in annual % returns between managers")
90 |
91 | rcParams.update({'font.size': 18})
92 |
93 |
94 |
95 | def file_process(filename):
96 | fig = plt.gcf()
97 | fig.set_size_inches(18.5,10.5)
98 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
99 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
100 |
101 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
102 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
103 |
104 |
105 | file_process("divbenefits")
106 |
107 | show()
108 |
--------------------------------------------------------------------------------
/plots_for_perhaps/diversification_stacked.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
4 | import Image
5 |
6 | def variance_f(sigma):
7 | x=[1.0/sigma.shape[0]]*sigma.shape[0]
8 | return 1.0/((np.matrix(x)*sigma*np.matrix(x).transpose())[0,0]**.5)
9 |
10 | def make_corr(dimension, offdiag=0.0):
11 |
12 | corr=np.array([[offdiag]*dimension]*dimension)
13 | corr[np.diag_indices(dimension)]=1.0
14 | return corr
15 |
16 | periodlabels=["Stocks", "Industries", "Countrys", "Regions"]
17 | cfactors=[0.85]*4+ [0.75]*3+ [0.5]*2 +[0.6]*2
18 | ndimlist=[1, 5, 10, 15, 1, 5, 10, 1, 5, 1, 3]
19 | ## take these values from diversification benefits plot
20 | basestd_stock=0.27
21 | basestd_industry=basestd_stock/1.082
22 | basestd_country=basestd_industry/1.136
23 | basestd_region=basestd_country/1.2909
24 | basestdlist=[basestd_stock]*4 + [basestd_industry]*3 + [basestd_country]*2 + [basestd_region]*2
25 |
26 |
27 | basearithmean=0.05
28 | riskfree=0.000
29 |
30 | ## apply blow up risk
31 |
32 | applyzero=[False]*(len(cfactors))
33 |
34 |
35 |
36 |
37 | results_sr=[]
38 | results_gmm=[]
39 | results_std=[]
40 |
41 | for ( basestd, appzero, ndim, cfactor) in zip(
42 | basestdlist, applyzero, ndimlist, cfactors):
43 |
44 | if appzero:
45 | gsr=0.0
46 | gmm=0.0
47 | new_std=basestd
48 | else:
49 | div_factor=variance_f(make_corr(ndim, cfactor))
50 | new_std=basestd/div_factor
51 | variance=new_std**2
52 | gmm=basearithmean- variance/2.0
53 | gsr=(gmm - riskfree) / new_std
54 |
55 |
56 |
57 | results_sr.append(gsr)
58 | results_gmm.append(gmm*100.0)
59 | results_std.append(new_std)
60 |
61 | print results_sr
62 | print results_gmm
63 | print results_std
64 |
65 |
66 | ## un stack up
67 | results_sr=[results_sr[0:4], [None]*3+results_sr[4:7], [None]*5+results_sr[7:9], [None]*6+results_sr[9:]]
68 | results_gmm=[results_gmm[0:4], [None]*3+ results_gmm[4:7], [None]*5+results_gmm[7:9], [None]*6+results_gmm[9:]]
69 | results_std=[results_std[0:4], [None]*3+results_std[4:7], [None]*5+results_std[7:9], [None]*6+results_std[9:]]
70 |
71 |
72 | from itertools import cycle
73 |
74 | lines = ["-", "--", "-.", ":"]
75 | linecycler = cycle(lines)
76 | colorcycler=cycle(["red", "blue", "green", "black"])
77 |
78 | for r in range(len(results_sr)):
79 | plot(results_sr[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
80 |
81 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
82 | ndimlist=["1", "5", "10", "15 / 1", "5", "10 / 1", "5 / 1", "3"]
83 | xticks(range(len(ndimlist)), ndimlist)
84 | legend(periodlabels, loc="top left", prop={'size': 18})
85 | #title("Diversification for various average correlations")
86 | ylabel("Sharpe ratio")
87 | xlabel("Number of assets")
88 |
89 | rcParams.update({'font.size': 18})
90 | ax=plt.gca()
91 | ax.get_legend().get_title().set_fontsize('18')
92 |
93 | def file_process(filename):
94 | fig = plt.gcf()
95 | fig.set_size_inches(18.5,10.5)
96 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
97 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
98 |
99 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
100 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
101 |
102 | file_process("divbenefit_sr_all_stacked")
103 |
104 | frame=plt.gca()
105 | frame.set_ylim([0.05, 0.3])
106 | frame.set_yticks([ 0.0, 0.1, 0.2, 0.3])
107 |
108 | show()
109 |
110 |
111 | for r in range(len(results_gmm)):
112 | plot(results_gmm[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
113 |
114 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
115 | xticks(range(len(ndimlist)), ndimlist)
116 | legend(periodlabels, loc="top left", prop={'size': 18})
117 | #title("Diversification for various average correlations")
118 | ylabel("Geometric mean %")
119 | xlabel("Number of assets")
120 |
121 | rcParams.update({'font.size': 18})
122 | ax=plt.gca()
123 | ax.get_legend().get_title().set_fontsize('18')
124 |
125 | frame=plt.gca()
126 | frame.set_ylim([1.0, 4.0])
127 | frame.set_yticks([ 1.0, 2.0, 3.0, 4.0])
128 |
129 |
130 | file_process("divbenefit_gmm_all_stacked")
131 |
132 | show()
133 |
134 | for r in range(len(results_std)):
135 | plot(results_std[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
136 |
137 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
138 | xticks(range(len(ndimlist)), ndimlist)
139 | legend(periodlabels, loc="top left", prop={'size': 18})
140 | #title("Diversification for various average correlations")
141 | ylabel("Standard deviation")
142 | xlabel("Number of assets")
143 |
144 | rcParams.update({'font.size': 18})
145 | ax=plt.gca()
146 | ax.get_legend().get_title().set_fontsize('18')
147 |
148 | file_process("divbenefit_std_all_stacked")
149 |
150 | show()
151 |
--------------------------------------------------------------------------------
/plots_for_perhaps/diversification_with_costs.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
4 | import Image
5 |
6 | def variance_f(sigma):
7 | x=[1.0/sigma.shape[0]]*sigma.shape[0]
8 | return 1.0/((np.matrix(x)*sigma*np.matrix(x).transpose())[0,0]**.5)
9 |
10 | def make_corr(dimension, offdiag=0.0):
11 |
12 | corr=np.array([[offdiag]*dimension]*dimension)
13 | corr[np.diag_indices(dimension)]=1.0
14 | return corr
15 |
16 | periodlabels=["Stocks", "Industries", "Countrys", "Regions"]
17 | cfactors=[0.85]*3+ [0.75]*3+ [0.85]*3 +[0.6]*2
18 | ndimlist=[1, 5, 10, 1, 5, 15, 1, 5, 10, 1, 4]
19 | ## take these values from diversification benefits plot
20 | basestd_stock=0.25
21 | basestd_10_stocks=0.232
22 | basestd_15_ind=basestd_10_stocks*(0.219/0.25)
23 | basestd_10_countries=basestd_15_ind*(0.232/0.25)
24 | basestdlist=[basestd_stock]*3 + [basestd_10_stocks]*3 + [basestd_15_ind]*3 + [basestd_10_countries]*2
25 |
26 |
27 | basearithmean=0.05
28 | riskfree=0.005
29 |
30 | ## apply blow up risk
31 |
32 | applyzero=[True]+[False]*(len(cfactors)-1)
33 |
34 |
35 |
36 |
37 | results_sr=[]
38 | results_gmm=[]
39 | results_std=[]
40 |
41 | for ( basestd, appzero, ndim, cfactor) in zip(
42 | basestdlist, applyzero, ndimlist, cfactors):
43 |
44 | if appzero:
45 | gsr=0.0
46 | gmm=0.0
47 | new_std=basestd
48 | else:
49 | div_factor=variance_f(make_corr(ndim, cfactor))
50 | new_std=basestd/div_factor
51 | variance=new_std**2
52 | gmm=basearithmean- variance/2.0
53 | gsr=(gmm - riskfree) / new_std
54 |
55 |
56 |
57 | results_sr.append(gsr)
58 | results_gmm.append(gmm*100.0)
59 | results_std.append(new_std)
60 |
61 | ## un stack up
62 | results_sr=[results_sr[0:3], [None]*3+results_sr[3:6], [None]*6+results_sr[6:9], [None]*9+results_sr[9:]]
63 | results_gmm=[results_gmm[0:3], [None]*3+ results_gmm[3:6], [None]*6+results_gmm[6:9], [None]*9+results_gmm[9:]]
64 | results_std=[results_std[0:3], [None]*3+results_std[3:6], [None]*6+results_std[6:9], [None]*9+results_std[9:]]
65 |
66 | from itertools import cycle
67 |
68 | lines = ["-", "--", "-.", ":"]
69 | linecycler = cycle(lines)
70 | colorcycler=cycle(["red", "blue", "green", "black"])
71 |
72 | for r in range(len(results_sr)):
73 | plot(results_sr[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
74 |
75 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
76 | xticks(range(len(ndimlist)), ndimlist)
77 | legend(periodlabels, loc="top left", prop={'size': 18})
78 | #title("Diversification for various average correlations")
79 | ylabel("Sharpe ratio")
80 | xlabel("Number of assets")
81 |
82 | rcParams.update({'font.size': 18})
83 | ax=plt.gca()
84 | ax.get_legend().get_title().set_fontsize('18')
85 |
86 | def file_process(filename):
87 | fig = plt.gcf()
88 | fig.set_size_inches(18.5,10.5)
89 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
90 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
91 |
92 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
93 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
94 |
95 | file_process("divbenefit_sr_all_stacked")
96 |
97 | show()
98 |
99 |
100 | for r in range(len(results_gmm)):
101 | plot(results_gmm[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
102 |
103 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
104 | xticks(range(len(ndimlist)), ndimlist)
105 | legend(periodlabels, loc="top left", prop={'size': 18})
106 | #title("Diversification for various average correlations")
107 | ylabel("Geometric mean %")
108 | xlabel("Number of assets")
109 |
110 | rcParams.update({'font.size': 18})
111 | ax=plt.gca()
112 | ax.get_legend().get_title().set_fontsize('18')
113 |
114 | file_process("divbenefit_gmm_all_stacked")
115 |
116 | show()
117 |
118 | for r in range(len(results_std)):
119 | plot(results_std[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
120 |
121 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
122 | xticks(range(len(ndimlist)), ndimlist)
123 | legend(periodlabels, loc="top left", prop={'size': 18})
124 | #title("Diversification for various average correlations")
125 | ylabel("Standard deviation")
126 | xlabel("Number of assets")
127 |
128 | rcParams.update({'font.size': 18})
129 | ax=plt.gca()
130 | ax.get_legend().get_title().set_fontsize('18')
131 |
132 | file_process("divbenefit_std_all_stacked")
133 |
134 | show()
135 |
--------------------------------------------------------------------------------
/plots_for_perhaps/diversificationbenefits.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
4 | import Image
5 |
6 | def variance_f(sigma):
7 | weights=[1.0/sigma.shape[0]]*sigma.shape[0]
8 | return 1.0/((np.matrix(weights)*sigma*np.matrix(weights).transpose())[0,0]**.5)
9 |
10 | def make_corr(dimension, offdiag=0.0):
11 |
12 | corr=np.array([[offdiag]*dimension]*dimension)
13 | corr[np.diag_indices(dimension)]=1.0
14 | return corr
15 |
16 |
17 | #cfactors=[0.1, 0.5, 0.6, 0.75, 0.8]
18 | #ndimlist=[1, 2, 3,4,5, 11, 15, 20]
19 | #maxdim=[3, 4, 10, 15, 20 ]
20 | cfactors=[.75]
21 | ndimlist=[1,11]
22 | maxdim=[100]
23 |
24 | #costs=[0.0005, 0.0033]
25 | costs=[0]*len(ndimlist)
26 |
27 | applyzero=[False, False, False, False, False, False, False, False, False]
28 |
29 | basestd=0.27
30 | basearithmean=0.05
31 | riskfree=0.000
32 |
33 | results_sr=[]
34 | results_gmm=[]
35 | results_std=[]
36 |
37 | for (cidx, cfactor) in enumerate(cfactors):
38 | smallres_sr=[]
39 | smallres_gmm=[]
40 | smallres_std=[]
41 |
42 | for nx, ndim in enumerate(ndimlist):
43 | if ndim<=maxdim[cidx]:
44 | if applyzero[cidx]:
45 | gsr=0.0
46 | gmm=0.0
47 | new_std=basestd
48 | else:
49 | div_factor=variance_f(make_corr(ndim, cfactor))
50 | new_std=basestd/div_factor
51 | variance=new_std**2
52 | gmm=basearithmean- costs[nx] - variance/2.0
53 | gsr=(gmm - riskfree) / new_std
54 |
55 | smallres_sr.append(gsr)
56 | smallres_gmm.append(gmm*100.0)
57 | smallres_std.append(new_std)
58 |
59 | print("Cfactor %f ndim %d GMM %f STD %f SR %f" % (cfactor, ndim, gmm, new_std, gsr))
60 |
61 | results_sr.append(smallres_sr)
62 | results_gmm.append(smallres_gmm)
63 | results_std.append(smallres_std)
64 |
65 |
66 | from itertools import cycle
67 |
68 | lines = ["-", "--", "-.", ":", "-"]
69 | linecycler = cycle(lines)
70 | colorcycler=cycle(["red", "blue", "green", "red", "blue"])
71 |
72 | for r in range(len(results_sr)):
73 | plot(results_sr[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
74 |
75 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
76 | xticks(range(len(ndimlist)), ndimlist)
77 | legend(cfactors, loc="top left", title="Correlations", prop={'size': 18})
78 | #title("Diversification for various average correlations")
79 | ylabel("Sharpe ratio")
80 | xlabel("Number of assets")
81 |
82 | frame=plt.gca()
83 | frame.set_ylim([0.05, 0.25])
84 | frame.set_yticks([ 0.05, 0.10, 0.15, 0.20, 0.25])
85 |
86 |
87 | rcParams.update({'font.size': 18})
88 | ax=plt.gca()
89 | ax.get_legend().get_title().set_fontsize('18')
90 |
91 | def file_process(filename):
92 | fig = plt.gcf()
93 | fig.set_size_inches(18.5,10.5)
94 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
95 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
96 |
97 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
98 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
99 |
100 | file_process("divbenefit_sr_all_unstacked")
101 |
102 | show()
103 |
104 |
105 | for r in range(len(results_gmm)):
106 | plot(results_gmm[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
107 |
108 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
109 | xticks(range(len(ndimlist)), ndimlist)
110 | legend(cfactors, loc="top left", title="Correlations", prop={'size': 18})
111 | #title("Diversification for various average correlations")
112 | ylabel("Geometric mean %")
113 | xlabel("Number of assets")
114 |
115 | rcParams.update({'font.size': 18})
116 | ax=plt.gca()
117 | ax.get_legend().get_title().set_fontsize('18')
118 |
119 | file_process("divbenefit_gmm_all_unstacked")
120 |
121 | show()
122 |
123 |
124 | for r in range(len(results_std)):
125 | plot(results_std[r], color=next(colorcycler), linestyle=next(linecycler), linewidth=3)
126 |
127 | #xticks(range(len(ndimlist))[0::2], ndimlist[0::2])
128 | xticks(range(len(ndimlist)), ndimlist)
129 | legend(cfactors, loc="top left", title="Correlations", prop={'size': 18})
130 | #title("Diversification for various average correlations")
131 | ylabel("Standard deviation")
132 | xlabel("Number of assets")
133 |
134 | rcParams.update({'font.size': 18})
135 | ax=plt.gca()
136 | ax.get_legend().get_title().set_fontsize('18')
137 |
138 | file_process("divbenefit_std_all_unstacked")
139 |
140 | show()
141 |
--------------------------------------------------------------------------------
/plots_for_perhaps/equitysectorweights.py:
--------------------------------------------------------------------------------
1 | from scipy.optimize import minimize
2 | from copy import copy
3 | import random
4 |
5 | import pandas as pd
6 | import numpy as np
7 | from datetime import datetime as dt
8 |
9 | def create_dull_pd_matrix(dullvalue=0.0, dullname="A", startdate=pd.datetime(1970,1,1).date(), enddate=dt.now().date(), index=None):
10 | """
11 | create a single valued pd matrix
12 | """
13 | if index is None:
14 | index=pd.date_range(startdate, enddate)
15 |
16 | dullvalue=np.array([dullvalue]*len(index))
17 |
18 | ans=pd.DataFrame(dullvalue, index, columns=[dullname])
19 |
20 | return ans
21 |
22 | def addem(weights):
23 | ## Used for constraints
24 | return 1.0 - sum(weights)
25 |
26 | def variance(weights, sigma):
27 | ## returns the variance (NOT standard deviation) given weights and sigma
28 | return (np.matrix(weights)*sigma*np.matrix(weights).transpose())[0,0]
29 |
30 |
31 | def neg_SR(weights, sigma, mus):
32 | ## Returns minus the Sharpe Ratio (as we're minimising)
33 |
34 | """
35 | estreturn=250.0*((np.matrix(x)*mus)[0,0])
36 | variance=(variance(x,sigma)**.5)*16.0
37 | """
38 | estreturn=(np.matrix(weights)*mus)[0,0]
39 | std_dev=(variance(weights,sigma)**.5)
40 |
41 |
42 | return -estreturn/std_dev
43 |
44 | def sigma_from_corr(std, corr):
45 | sigma=std*corr*std
46 |
47 | return sigma
48 |
49 | def basic_opt(std,corr,mus):
50 | number_assets=mus.shape[0]
51 | sigma=sigma_from_corr(std, corr)
52 | start_weights=[1.0/number_assets]*number_assets
53 |
54 | ## Constraints - positive weights, adding to 1.0
55 | bounds=[(0.0,1.0)]*number_assets
56 | cdict=[{'type':'eq', 'fun':addem}]
57 |
58 | return minimize(neg_SR_riskfree, start_weights, (sigma, mus), method='SLSQP', bounds=bounds, constraints=cdict, tol=0.00001)
59 |
60 | def neg_SR_riskfree(weights, sigma, mus, riskfree=0.005):
61 | ## Returns minus the Sharpe Ratio (as we're minimising)
62 |
63 | """
64 | estreturn=250.0*((np.matrix(x)*mus)[0,0])
65 | variance=(variance(x,sigma)**.5)*16.0
66 | """
67 | estreturn=(np.matrix(weights)*mus)[0,0] - riskfree
68 | std_dev=(variance(weights,sigma)**.5)
69 |
70 |
71 | return -estreturn/std_dev
72 |
73 |
74 | def equalise_vols(returns, default_vol):
75 | """
76 | Normalises returns so they have the in sample vol of defaul_vol (annualised)
77 | Assumes daily returns
78 | """
79 |
80 | factors=(default_vol/16.0)/returns.std(axis=0)
81 | facmat=create_dull_pd_matrix(dullvalue=factors, dullname=returns.columns, index=returns.index)
82 | norm_returns=returns*facmat
83 | norm_returns.columns=returns.columns
84 |
85 | return norm_returns
86 |
87 |
88 | def offdiag_matrix(offvalue, nlength):
89 | identity=np.diag([1.0]*nlength)
90 | for x in range(nlength):
91 | for y in range(nlength):
92 | if x!=y:
93 | identity[x][y]=offvalue
94 | return identity
95 |
96 | def get_avg_corr(sigma):
97 | new_sigma=copy(sigma)
98 | np.fill_diagonal(new_sigma,np.nan)
99 | return np.nanmean(new_sigma)
100 |
101 |
102 |
103 | def read_ts_csv(fname, dindex="Date"):
104 | data=pd.read_csv(fname)
105 | dateindex=[dt.strptime(dx, "%d/%m/%y") for dx in list(data[dindex])]
106 | data.index=dateindex
107 | del(data[dindex])
108 |
109 | return data
110 |
111 | def markosolver(returns, equalisemeans=False, equalisevols=True, default_vol=0.2, default_SR=1.0):
112 | """
113 | Returns the optimal portfolio for the dataframe returns
114 |
115 | If equalisemeans=True then assumes all assets have same return if False uses the asset means
116 |
117 | If equalisevols=True then normalises returns to have same standard deviation; the weights returned
118 | will be 'risk weightings'
119 |
120 | Note if usemeans=True and equalisevols=True effectively assumes all assets have same sharpe ratio
121 |
122 | """
123 |
124 | if equalisevols:
125 | use_returns=equalise_vols(returns, default_vol)
126 | else:
127 | use_returns=returns
128 |
129 |
130 | ## Sigma matrix
131 | sigma=use_returns.cov().values
132 |
133 | ## Expected mean returns
134 | est_mus=[use_returns[asset_name].mean() for asset_name in use_returns.columns]
135 | missingvals=[np.isnan(x) for x in est_mus]
136 |
137 |
138 | if equalisemeans:
139 | ## Don't use the data - Set to the average Sharpe Ratio
140 | mus=[default_vol*default_SR]*returns.shape[1]
141 |
142 | else:
143 | mus=est_mus
144 |
145 | mus=np.array(mus, ndmin=2).transpose()
146 |
147 |
148 | ## Starting weights
149 | number_assets=use_returns.shape[1]
150 | start_weights=[1.0/number_assets]*number_assets
151 |
152 | ## Constraints - positive weights, adding to 1.0
153 | bounds=[(0.0,1.0)]*number_assets
154 | cdict=[{'type':'eq', 'fun':addem}]
155 |
156 | ans=minimize(neg_SR, start_weights, (sigma, mus), method='SLSQP', bounds=bounds, constraints=cdict, tol=0.00001)
157 | wts=ans['x']
158 |
159 | return wts
160 |
161 | def bootstrap_portfolio(returns_to_bs, monte_carlo=500, monte_length=25, equalisemeans=False, equalisevols=True, default_vol=0.2, default_SR=1.0):
162 |
163 | """
164 | Given dataframe of returns; returns_to_bs, performs a bootstrap optimisation
165 |
166 | We run monte_carlo numbers of bootstraps
167 | Each one contains monte_length days drawn randomly, with replacement
168 | (so *not* block bootstrapping)
169 |
170 | The other arguments are passed to the optimisation function markosolver
171 |
172 | Note - doesn't deal gracefully with missing data. Will end up downweighting stuff depending on how
173 | much data is missing in each boostrap. You'll need to think about how to solve this problem.
174 |
175 | """
176 |
177 |
178 | weightlist=[]
179 | for unused_index in range(monte_carlo):
180 | bs_idx=[int(random.uniform(0,1)*len(returns_to_bs)) for i in range(monte_length)]
181 |
182 | returns=returns_to_bs.iloc[bs_idx,:]
183 | weight=markosolver(returns, equalisemeans=equalisemeans, equalisevols=equalisevols, default_vol=default_vol, default_SR=default_SR)
184 | weightlist.append(weight)
185 |
186 | ### We can take an average here; only because our weights always add up to 1. If that isn't true
187 | ### then you will need to some kind of renormalisation
188 |
189 | theweights_mean=list(np.mean(weightlist, axis=0))
190 | return theweights_mean
191 |
192 |
193 | rawdata=read_ts_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/sectorreturns.csv")
194 | rawdata=rawdata/100.0
195 |
196 | bootstrap_portfolio(rawdata, equalisemeans=True, equalisevols=True, default_vol=0.2, default_SR=1.0)
--------------------------------------------------------------------------------
/plots_for_perhaps/etfvssharesbreakeven.csv:
--------------------------------------------------------------------------------
1 | Value,Stock cost,ETF cost
2 | 10000,0.26,0.17
3 | 12000,0.21,0.17
4 | 14000,0.18,0.17
5 | 16000,0.16,0.17
6 | 18000,0.14,0.17
7 | 20000,0.07,0.17
8 |
--------------------------------------------------------------------------------
/plots_for_perhaps/etfvssharesbreakevenwithtrading.csv:
--------------------------------------------------------------------------------
1 | Value,Stock cost,ETF cost
2 | 100000,0.54,0.17
3 | 150000,0.36,0.17
4 | 200000,0.27,0.17
5 | 250000,0.22,0.17
6 | 300000,0.19,0.17
7 | 325000,0.17,0.17
8 | 350000,0.16,0.17
9 | 400000,0.16,0.17
10 | 500000,0.16,0.17
11 |
--------------------------------------------------------------------------------
/plots_for_perhaps/fanchart.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | from random import random
4 |
5 | def pd_readcsv(filename, date_index_name="DATETIME"):
6 | """
7 | Reads a pandas data frame, with time index labelled
8 | package_name(/path1/path2.., filename
9 |
10 | :param filename: Filename with extension
11 | :type filename: str
12 |
13 | :param date_index_name: Column name of date index
14 | :type date_index_name: list of str
15 |
16 |
17 | :returns: pd.DataFrame
18 |
19 | """
20 |
21 | ans = pd.read_csv(filename)
22 | #ans.index = pd.to_datetime(ans[date_index_name]).values
23 | ans.index = ans[date_index_name].values
24 |
25 | del ans[date_index_name]
26 |
27 | ans.index.name = None
28 |
29 | return ans
30 |
31 | def finalpr(x):
32 | if type(x) is pd.core.frame.DataFrame:
33 | return x.apply(geomean, 0)
34 | prod_returns=x+1.0
35 | cum_prod_returns=prod_returns.cumprod()
36 | final_pr=cum_prod_returns.values[-1]
37 |
38 | return final_pr
39 |
40 | def geomean(x):
41 | final_pr=finalpr(x)
42 | avg_pr=final_pr**(1/float(len(x.index)))
43 | return avg_pr-1.0
44 |
45 | def geostd(x):
46 | if type(x) is pd.core.frame.DataFrame:
47 | return x.apply(geostd, 0)
48 | gm=geomean(x)
49 |
50 | def _gsone(xitem, gm):
51 | return (np.log((1+xitem)/(1+gm)))**2.0
52 |
53 | items=[_gsone(xitem, gm) for xitem in x.values]
54 |
55 | return np.exp((np.mean(items))**.5)-1.0
56 |
57 |
58 | data=pd_readcsv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/USrealreturnsADJ.csv", "year")
59 |
60 | bond_weight, equity_weight=[0.68, 0.32]
61 |
62 | ## resampled version
63 | period_length=21
64 | monte_runs=5000
65 |
66 | results=[[1000.0, 1000.0, 1000.0]]
67 | for period in range(period_length)[1:]:
68 | print(period)
69 | periodres=[]
70 | for monte in range(monte_runs):
71 | choose=[int(random()*len(data.index)) for notused in range(period)]
72 | subdata=data.iloc[choose,]
73 | subdata.index=pd.date_range(pd.datetime(1990,1,1), periods=period, freq="A")
74 |
75 | portfolio_returns=subdata.SP500*equity_weight+subdata.US10*bond_weight
76 | gmeans=finalpr(portfolio_returns)
77 | periodres.append(gmeans)
78 |
79 | med=np.percentile(periodres,50)*1000
80 | m5=np.percentile(periodres,5)*1000
81 | m95=np.percentile(periodres,95)*1000
82 |
83 | results.append([m5,med,m95])
84 |
85 | results=np.array(results)
86 | results=pd.DataFrame(results, pd.date_range(pd.datetime(2017,1,1), periods=period_length, freq="A"))
87 | results.columns=["Lower 5%", "Median", "Upper 5%"]
88 |
89 | results.iloc[:,0].plot(style="k-.")
90 | results.iloc[:,1].plot(style="k-")
91 | results.iloc[:,2].plot(style="k-.")
92 |
93 |
94 | from optimisation import *
95 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
96 | import matplotlib.pyplot as plt
97 | import Image
98 |
99 | def file_process(filename):
100 | fig = plt.gcf()
101 | fig.set_size_inches(18.5,10.5)
102 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
103 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
104 |
105 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
106 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
107 |
108 |
109 | frame=plt.gca()
110 |
111 | frame.set_yticks([1000.0, 3000., 5000.])
112 |
113 |
114 |
115 | rcParams.update({'font.size': 18})
116 |
117 | file_process("fanchart")
118 |
119 | show()
120 |
--------------------------------------------------------------------------------
/plots_for_perhaps/hist_Estimate_new.py:
--------------------------------------------------------------------------------
1 | from optimisation import *
2 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
3 | import matplotlib.pyplot as plt
4 | import Image
5 |
6 | from itertools import cycle
7 |
8 | from datetime import datetime as dt
9 | from twisted.test.test_amp import THING_I_DONT_UNDERSTAND
10 |
11 |
12 |
13 | lines = ["-","--"]
14 | linecycler = cycle(lines)
15 | colorcycler=cycle(["red", "blue"])
16 |
17 | def read_ts_csv(fname, dindex="Date"):
18 | data=pd.read_csv(fname)
19 | dateindex=[dt.strptime(dx, "%d/%m/%y") for dx in list(data[dindex])]
20 | data.index=dateindex
21 | del(data[dindex])
22 |
23 | return data
24 |
25 | def calc_asset_returns(rawdata, tickers):
26 | asset_returns=pd.concat([get_monthly_tr(tickname, rawdata) for tickname in tickers], axis=1)
27 | asset_returns.columns=tickers
28 |
29 | return asset_returns
30 |
31 | def get_monthly_tr(tickname, rawdata):
32 | total_returns=rawdata[tickname+"_TR"]
33 | return (total_returns / total_returns.shift(1)) - 1.0
34 |
35 | def pd_readcsv(filename, date_index_name="DATETIME"):
36 | """
37 | Reads a pandas data frame, with time index labelled
38 | package_name(/path1/path2.., filename
39 |
40 | :param filename: Filename with extension
41 | :type filename: str
42 |
43 | :param date_index_name: Column name of date index
44 | :type date_index_name: list of str
45 |
46 |
47 | :returns: pd.DataFrame
48 |
49 | """
50 |
51 | ans = pd.read_csv(filename)
52 | #ans.index = pd.to_datetime(ans[date_index_name]).values
53 | ans.index = ans[date_index_name].values
54 |
55 | del ans[date_index_name]
56 |
57 | ans.index.name = None
58 |
59 | return ans
60 |
61 |
62 | data=pd_readcsv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/USrealreturns.csv", "year")
63 | data=data[['SP500','US10']]
64 |
65 | def linehist(x, color="blue", linestyle="-"):
66 | y,binEdges =np.histogram(x, bins=50)
67 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
68 | plot(bincenters,y,'-', color=color, linestyle=linestyle)
69 |
70 |
71 | """
72 | Plot bootstrapped statistics
73 |
74 | Awful code only works with 3 assets
75 | """
76 |
77 | def finalpr(x):
78 | prod_returns=x+1.0
79 | cum_prod_returns=prod_returns.cumprod()
80 | final_pr=cum_prod_returns.values[-1]
81 |
82 | return final_pr
83 |
84 | def geomean(x):
85 | if type(x) is pd.core.frame.DataFrame:
86 | return x.apply(geomean, 0)
87 |
88 | final_pr=finalpr(x)
89 | avg_pr=final_pr**(1/float(len(x.index)))
90 | return avg_pr-1.0
91 |
92 |
93 | srs=[]
94 | stds=[]
95 | corrs=[]
96 | gmeans=[]
97 | gdiff=[]
98 | gdiffsr=[]
99 |
100 | monte_carlo=10000
101 |
102 | monte_length=len(data)
103 |
104 | for unused_index in range(monte_carlo):
105 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
106 | returns=data.iloc[bs_idx,:]
107 | ## GEOMETRIC
108 | gm=geomean(returns)
109 | gmeans.append(list(gm))
110 | gdiff.append(gm[0] - gm[1])
111 | st=returns.std()
112 | stds.append(list(st))
113 | sr=gm/st
114 | srs.append(list(sr))
115 | gdiffsr.append(sr[0] - sr[1])
116 | cm=returns.corr().values
117 | corrs.append([cm[0][1]])
118 |
119 | codes=data.columns
120 |
121 |
122 | gmeans=np.array(gmeans)
123 | stds=np.array(stds)
124 | corrs=np.array(corrs)
125 | srs=np.array(srs)
126 | gdiff=np.array(gdiff)
127 | gdiffsr=np.array(gdiffsr)
128 |
129 |
130 | linehist(gmeans[:,0], linestyle=next(linecycler), color=next(colorcycler))
131 | linehist(gmeans[:,1], linestyle=next(linecycler), color=next(colorcycler))
132 |
133 |
134 | frame=plt.gca()
135 |
136 | frame.get_yaxis().set_visible(False)
137 | frame.set_xticks([0.0, 0.10, 0.2])
138 |
139 | legend(codes)
140 |
141 | xlabel("Annualised geometric return")
142 |
143 | rcParams.update({'font.size': 18})
144 |
145 |
146 | def file_process(filename):
147 | fig = plt.gcf()
148 | fig.set_size_inches(18.5,10.5)
149 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
150 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
151 |
152 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
153 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
154 |
155 | file_process("meanhist")
156 |
157 |
158 | show()
159 |
160 |
161 | linehist(stds[:,0], linestyle=next(linecycler), color=next(colorcycler))
162 | linehist(stds[:,1], linestyle=next(linecycler), color=next(colorcycler))
163 | legend(codes)
164 |
165 | frame=plt.gca()
166 |
167 | frame.get_yaxis().set_visible(False)
168 | frame.set_xticks([ 0.0, 0.1, 0.2, 0.3])
169 |
170 |
171 | print np.percentile(stds[:,0], 5)
172 | print np.percentile(stds[:,0], 95)
173 | print np.percentile(stds[:,1], 5)
174 | print np.percentile(stds[:,1], 95)
175 |
176 |
177 | xlabel("Annualised standard deviation")
178 | rcParams.update({'font.size': 18})
179 |
180 | file_process("stdhist")
181 |
182 | show()
183 |
184 |
185 | linehist(corrs[:,0])
186 |
187 |
188 | xlabel("Correlation")
189 |
190 |
191 | frame=plt.gca()
192 |
193 | frame.get_yaxis().set_visible(False)
194 | frame.set_xticks([ -0.25, 0, 0.25])
195 |
196 | print np.percentile(corrs[:,0], 5)
197 | print np.percentile(corrs[:,0], 95)
198 |
199 |
200 | rcParams.update({'font.size': 18})
201 |
202 | file_process("corrdist")
203 |
204 | show()
205 |
206 | linehist(srs[:,0], linestyle=next(linecycler), color=next(colorcycler))
207 | linehist(srs[:,1], linestyle=next(linecycler), color=next(colorcycler))
208 | legend(codes)
209 |
210 | frame=plt.gca()
211 |
212 | frame.get_yaxis().set_visible(False)
213 | frame.set_xticks([0.0, 0.5])
214 |
215 |
216 | xlabel("Sharpe Ratio")
217 |
218 | rcParams.update({'font.size': 18})
219 |
220 | file_process("SRdist")
221 |
222 | show()
223 |
224 |
225 |
226 | linehist(list(gdiff))
227 |
228 |
229 | xlabel("Difference in geometric means")
230 |
231 |
232 | frame=plt.gca()
233 |
234 | frame.get_yaxis().set_visible(False)
235 | frame.set_xticks([ 0.0, 0.1])
236 |
237 |
238 |
239 | rcParams.update({'font.size': 18})
240 |
241 | file_process("gdiffdist")
242 |
243 | show()
244 |
245 |
246 |
247 |
248 | linehist(list(gdiffsr))
249 |
250 |
251 | xlabel("Difference in Sharpe Ratios")
252 |
253 |
254 | frame=plt.gca()
255 |
256 | frame.get_yaxis().set_visible(False)
257 | frame.set_xticks([ -.5, 0.0, .5])
258 |
259 |
260 |
261 | rcParams.update({'font.size': 18})
262 |
263 | file_process("diffsrdist")
264 |
265 | print np.percentile(list(gdiffsr), 5)
266 | print np.percentile(list(gdiffsr), 95)
267 |
268 | show()
269 |
270 |
--------------------------------------------------------------------------------
/plots_for_perhaps/hist_estimates.py:
--------------------------------------------------------------------------------
1 | from optimisation import *
2 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
3 | import matplotlib.pyplot as plt
4 | import Image
5 |
6 | from itertools import cycle
7 |
8 | from datetime import datetime as dt
9 | from twisted.test.test_amp import THING_I_DONT_UNDERSTAND
10 |
11 |
12 |
13 | lines = ["-","--","-."]
14 | linecycler = cycle(lines)
15 | colorcycler=cycle(["red", "blue", "green"])
16 |
17 | def read_ts_csv(fname, dindex="Date"):
18 | data=pd.read_csv(fname)
19 | dateindex=[dt.strptime(dx, "%d/%m/%y") for dx in list(data[dindex])]
20 | data.index=dateindex
21 | del(data[dindex])
22 |
23 | return data
24 |
25 | def calc_asset_returns(rawdata, tickers):
26 | asset_returns=pd.concat([get_monthly_tr(tickname, rawdata) for tickname in tickers], axis=1)
27 | asset_returns.columns=tickers
28 |
29 | return asset_returns
30 |
31 | def get_monthly_tr(tickname, rawdata):
32 | total_returns=rawdata[tickname+"_TR"]
33 | return (total_returns / total_returns.shift(1)) - 1.0
34 |
35 | rawdata=read_ts_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/MSCI_data.csv")
36 |
37 | data=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/USmonthlyreturns.csv")
38 | data = data[1188:]
39 | dindex="Date"
40 | dateindex=[dt.strptime(dx, "%d/%m/%Y") for dx in list(data[dindex])]
41 | data.index=dateindex
42 | del(data[dindex])
43 |
44 |
45 | asset_returns2=calc_asset_returns(rawdata, ["UK"])
46 | asset_returns2.columns=["UK_Equity"]
47 |
48 |
49 | asset_returns=data[["SP_TR", "Bond_TR"]]
50 | asset_returns.columns=["US_Equity", "US_Bond"]
51 |
52 | asset_returns.index = [x + pd.DateOffset(months=1) for x in asset_returns.index]
53 | asset_returns.index = [x - pd.DateOffset(days=1) for x in asset_returns.index]
54 |
55 |
56 | data=pd.concat([asset_returns, asset_returns2], axis=1)
57 | data = data.cumsum().ffill().reindex(asset_returns.index).diff()
58 |
59 | data.columns=["US Equity" ,"US Bond", "UK Equity"]
60 |
61 | def linehist(x, color="blue", linestyle="-"):
62 | y,binEdges =np.histogram(x)
63 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
64 | plot(bincenters,y,'-', color=color, linestyle=linestyle)
65 |
66 |
67 | """
68 | Plot bootstrapped statistics
69 |
70 | Awful code only works with 3 assets
71 | """
72 |
73 |
74 | srs=[]
75 | means=[]
76 | stds=[]
77 | corrs=[]
78 |
79 | monte_carlo=100
80 | monte_length=len(data)
81 |
82 | for unused_index in range(monte_carlo):
83 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
84 | returns=data.iloc[bs_idx,:]
85 | means.append(list(12.0*returns.mean()))
86 | stds.append(list((12**.5)*returns.std()))
87 | srs.append(list((12**.5)*returns.mean()/returns.std()))
88 | cm=returns.corr().values
89 | corrs.append([cm[0][1], cm[0][2],cm[1][2]])
90 |
91 | codes=data.columns
92 | corrnames=["US Equity / US Bond", "US Equity / UK Equity", "US Bond / UK Equity"]
93 |
94 | means=np.array(means)
95 | stds=np.array(stds)
96 | corrs=np.array(corrs)
97 | srs=np.array(srs)
98 |
99 | linehist(means[:,0], linestyle=next(linecycler), color=next(colorcycler))
100 | linehist(means[:,1], linestyle=next(linecycler), color=next(colorcycler))
101 | linehist(means[:,2], linestyle=next(linecycler), color=next(colorcycler))
102 |
103 | frame=plt.gca()
104 |
105 | frame.get_yaxis().set_visible(False)
106 | frame.set_xticks([0.0, 0.10, 0.2])
107 |
108 | legend(codes)
109 |
110 | xlabel("Annualised return")
111 |
112 | rcParams.update({'font.size': 18})
113 |
114 |
115 | def file_process(filename):
116 | fig = plt.gcf()
117 | fig.set_size_inches(18.5,10.5)
118 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
119 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
120 |
121 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
122 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
123 |
124 | file_process("meanhist")
125 |
126 |
127 | show()
128 |
129 |
130 | linehist(stds[:,0], linestyle=next(linecycler), color=next(colorcycler))
131 | linehist(stds[:,1], linestyle=next(linecycler), color=next(colorcycler))
132 | linehist(stds[:,2], linestyle=next(linecycler), color=next(colorcycler))
133 | legend(codes)
134 |
135 | frame=plt.gca()
136 |
137 | frame.get_yaxis().set_visible(False)
138 | frame.set_xticks([ 0.0, 0.1, 0.2, 0.3])
139 |
140 |
141 |
142 | xlabel("Annualised standard deviation")
143 | rcParams.update({'font.size': 18})
144 |
145 | file_process("stdhist")
146 |
147 | show()
148 |
149 | linehist(corrs[:,0], linestyle=next(linecycler), color=next(colorcycler))
150 | linehist(corrs[:,1], linestyle=next(linecycler), color=next(colorcycler))
151 | linehist(corrs[:,2], linestyle=next(linecycler), color=next(colorcycler))
152 | legend(corrnames, loc="upper center")
153 |
154 | xlabel("Correlation")
155 |
156 |
157 | frame=plt.gca()
158 |
159 | frame.get_yaxis().set_visible(False)
160 | frame.set_xticks([ -0.0, 0.25, 0.5])
161 |
162 |
163 |
164 | rcParams.update({'font.size': 18})
165 |
166 | file_process("corrdist")
167 |
168 | show()
169 |
170 | linehist(srs[:,0], linestyle=next(linecycler), color=next(colorcycler))
171 | linehist(srs[:,1], linestyle=next(linecycler), color=next(colorcycler))
172 | linehist(srs[:,2], linestyle=next(linecycler), color=next(colorcycler))
173 | legend(codes)
174 |
175 | frame=plt.gca()
176 |
177 | frame.get_yaxis().set_visible(False)
178 | frame.set_xticks([0.0, 0.5, 1.0])
179 |
180 |
181 | xlabel("Sharpe Ratio")
182 |
183 | rcParams.update({'font.size': 18})
184 |
185 | file_process("SRdist")
186 |
187 | show()
188 |
189 |
190 | ## relative_div
191 |
192 | def relative_item(data, func=None, usecorr=False):
193 |
194 | if usecorr:
195 | mean_ts = mycorr(data)
196 | else:
197 | mean_ts=pd.rolling_apply(data, 60, func)
198 |
199 | avg_ts=mean_ts.mean(axis=1)
200 | avg_ts=pd.concat([avg_ts, avg_ts, avg_ts], axis=1)
201 | avg_ts.columns = mean_ts.columns
202 |
203 | if usecorr:
204 | mean_ts=mean_ts - avg_ts
205 | else:
206 | mean_ts=mean_ts / avg_ts
207 |
208 | return mean_ts
209 |
210 | def sr(xdata):
211 | return (12**.5)*np.mean(xdata)/np.std(xdata)
212 |
213 | def mycorr(data):
214 | c1=pd.rolling_corr(data['US Equity'], data['US Bond'], window=60)
215 | c2=pd.rolling_corr(data['US Equity'], data['UK Equity'], window=60)
216 | c3=pd.rolling_corr(data['US Bond'], data['UK Equity'], window=60)
217 |
218 | thing = pd.concat([c1,c2,c3], axis=1)
219 | thing.columns=["US Equity / US Bond", "US Equity / UK Equity", "US Bond / UK Equity"]
220 |
221 | return thing
222 |
223 | mean_ts=relative_item(data, np.mean)
224 | mean_ts.plot()
225 | show()
226 |
227 | std_ts=relative_item(data, np.std)
228 | std_ts.plot()
229 | show()
230 |
231 | sr_ts = relative_item(data, sr)
232 | sr_ts.plot()
233 | show()
234 |
235 | corr_ts=relative_item(data, usecorr=True)
236 | corr_ts.plot()
237 |
238 | #frame=plt.gca()
239 | #frame.set_ylim([-1.0, 1.0])
240 | #frame.set_yticks([-1.0, 0.0, 1.0])
241 |
242 | show()
243 |
244 |
--------------------------------------------------------------------------------
/plots_for_perhaps/histofinvestmentgame.py:
--------------------------------------------------------------------------------
1 | import random
2 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
3 | import matplotlib.pyplot as plt
4 | import Image
5 | import numpy as np
6 |
7 | from itertools import cycle
8 |
9 | lines = ["-","--","-."]
10 | linecycler = cycle(lines)
11 | colorcycler=cycle(["red", "blue", "green"])
12 |
13 | def linehist(x, color="blue", linestyle="-", bins=10, linewidth=1):
14 | y,binEdges =np.histogram(x, bins=bins)
15 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
16 | plot(bincenters,y,'-', color=color, linestyle=linestyle, linewidth=linewidth)
17 |
18 | data=[-.6, -.5,
19 | -.3, -.3, -.3,
20 | -.2, -.2,
21 | -.1,-.1,-.1,-.1,
22 | 0.0, 0.0,0.0, 0.0,0.0,0.0,
23 | .1, .1,.1,.1,.1,
24 | .2,.2,.2,
25 | .3,.3,.3,.3,
26 | .4,.4,.4,.7, .8, .8]
27 |
28 | linehist(data, linewidth=2)
29 | frame=plt.gca()
30 |
31 | frame.get_yaxis().set_visible(False)
32 | frame.set_xlim([-0.8, 0.8])
33 | frame.set_xticks([-.8, -.4, 0.0,.4, 0.8])
34 |
35 |
36 |
37 |
38 | rcParams.update({'font.size': 18})
39 |
40 |
41 | def file_process(filename):
42 | fig = plt.gcf()
43 | fig.set_size_inches(18.5,10.5)
44 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
45 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
46 |
47 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
48 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
49 |
50 | file_process("investmentgame")
51 | show()
52 |
53 | mu=np.mean(data)
54 | sigma=np.std(data)
55 | x = np.linspace(-2, 2, 100)
56 | import matplotlib.mlab as mlab
57 | y=mlab.normpdf(x, mu, sigma)
58 | y=[yvalue * 5.0 for yvalue in y]
59 |
60 | linehist(data, linewidth=2)
61 | plt.plot(x,y, linewidth=3, linestyle="dashed", color="gray")
62 |
63 | plt.legend(['Actual','Gaussian distribution'])
64 |
65 | frame=plt.gca()
66 |
67 | frame.get_yaxis().set_visible(False)
68 | frame.set_xlim([-1.2, 1.2])
69 | frame.set_xticks([-1.0, -.5, 0.0,.5, 1.0])
70 |
71 |
72 | file_process("investmentgame_gauss")
73 | show()
74 |
75 |
76 | monte_carlo=100000
77 | monte_length=35
78 | avgs=[]
79 | for unused_index in range(monte_carlo):
80 | returns=[random.gauss(np.mean(data),np.std(data)) for not_used in range(monte_length)]
81 | avgs.append(np.mean(returns))
82 |
83 | avgs.sort()
84 | morethan=[x for x in avgs if x<0.0]
85 | ratio=float(len(morethan))/float(monte_carlo)
86 | print("Proportion %f" % ratio)
87 | print("Std %f" % np.std(avgs))
88 |
89 | linehist(avgs, bins=100, linewidth=2)
90 | frame=plt.gca()
91 |
92 | frame.get_yaxis().set_visible(False)
93 | frame.set_xlim([-0.1, 0.3])
94 | frame.set_xticks([-.1, 0.0, 0.1,.2, 0.3])
95 |
96 | #frame.annotate("Actual return 0.086", xy=(0.086, 12000),xytext=(0.086, 25000), arrowprops=dict(facecolor='black', shrink=0.05))
97 |
98 |
99 | rcParams.update({'font.size': 18})
100 |
101 | file_process("ismeandifferent")
102 |
103 | show()
--------------------------------------------------------------------------------
/plots_for_perhaps/msciworld.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def variance_f(sigma, weights):
4 | return 1.0/((np.matrix(weights)*sigma*np.matrix(weights).transpose())[0,0]**.5)
5 |
6 | def make_corr(dimension, offdiag=0.0):
7 |
8 | corr=np.array([[offdiag]*dimension]*dimension)
9 | corr[np.diag_indices(dimension)]=1.0
10 | return corr
11 |
12 |
13 | cfactor=0.80
14 | basestd=0.25
15 | basearithmean=0.05
16 | riskfree=0.005
17 |
18 |
19 | def calc_stats(weights, basestd, basearithmean, corrmatrix=None, cfactor=1.0):
20 | if corrmatrix is None:
21 | universe_size=len(weights)
22 | corrmatrix=make_corr(universe_size, cfactor)
23 | div_factor=variance_f(corrmatrix, weights)
24 | new_std=basestd/div_factor
25 | variance=new_std**2
26 | gmm=basearithmean- variance/2.0
27 | gsr=(gmm - riskfree) / new_std
28 |
29 | return (gmm, new_std, gsr)
30 |
31 |
32 | ### Use MSCI world weights
33 | import pandas as pd
34 | tsx_weights=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/MSCIWorldweights.csv")
35 |
36 | universe_size=len(tsx_weights.index)
37 |
38 | corrmatrix=np.zeros((universe_size, universe_size))
39 |
40 | for i in range(universe_size):
41 | for j in range(universe_size):
42 | if i==j:
43 | corr=1.0
44 | elif tsx_weights.irow(i).Region==tsx_weights.irow(j).Region:
45 | corr=0.85
46 | else:
47 | corr=0.60
48 | corrmatrix[i][j]=corr
49 |
50 | ## top down weights
51 | regions=list(tsx_weights.Region.unique())
52 | weight_each_region=1.0/len(regions)
53 |
54 | region_counts=dict([(region, sum(tsx_weights.Region==region)) for region in regions])
55 |
56 | topdown_weights=[]
57 | for (tidx, region) in enumerate(tsx_weights.Region):
58 | topdown_weights.append(weight_each_region * (1.0/region_counts[region]))
59 |
60 | ##cap weights
61 | cap_weights=list(tsx_weights.weight.values)
62 | cap_weights=cap_weights/sum(cap_weights)
63 | equal_weights=[1.0/universe_size]*universe_size
64 |
65 | print calc_stats(cap_weights, basestd, basearithmean, corrmatrix)
66 | print calc_stats(equal_weights, basestd, basearithmean, corrmatrix)
67 | print calc_stats(topdown_weights, basestd, basearithmean, corrmatrix)
68 |
--------------------------------------------------------------------------------
/plots_for_perhaps/plotcontango.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import datetime as dt
3 | import Image
4 | from random import gauss
5 | import numpy as np
6 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, scatter
7 |
8 | import matplotlib.pylab as plt
9 | from itertools import cycle
10 | import pickle
11 | import pandas as pd
12 |
13 | lines = ["--","-","-."]
14 | linecycler = cycle(lines)
15 |
16 |
17 | import Quandl
18 |
19 |
20 | def get_quandl(code):
21 | QUANDLDICT=dict(GOLD="COM/WLD_GOLD", CORN="COM/PMAIZMT_USD", CRUDE_W="COM/WLD_CRUDE_WTI")
22 | authtoken='qWXuZcdwzwQ2GJQ88sNb'
23 | quandldef=QUANDLDICT[code]
24 | print quandldef
25 | data = Quandl.get(quandldef, authtoken=authtoken)
26 |
27 | data.columns=["value"]
28 | return data.iloc[:,0]
29 |
30 |
31 | def pd_readcsv(filename, date_index_name="DATETIME"):
32 | """
33 | Reads a pandas data frame, with time index labelled
34 | package_name(/path1/path2.., filename
35 |
36 | :param filename: Filename with extension
37 | :type filename: str
38 |
39 | :param date_index_name: Column name of date index
40 | :type date_index_name: list of str
41 |
42 |
43 | :returns: pd.DataFrame
44 |
45 | """
46 |
47 | ans = pd.read_csv(filename)
48 | ans.index = pd.to_datetime(ans[date_index_name]).values
49 |
50 | del ans[date_index_name]
51 |
52 | ans.index.name = None
53 |
54 | return ans
55 |
56 |
57 | def get_spot_price(instrument_code):
58 | datapath= "/home/rob/workspace3/pysystemtrade/sysdata/legacycsv/"
59 | filename = datapath+ instrument_code + "_carrydata.csv"
60 | instrcarrydata = pd_readcsv(filename)
61 |
62 | return instrcarrydata.PRICE
63 |
64 | def get_adj_price(instrument_code):
65 | datapath= "/home/rob/workspace3/pysystemtrade/sysdata/legacycsv/"
66 | filename = datapath+ instrument_code + "_price.csv"
67 | instrprice = pd_readcsv(filename)
68 | instrprice.columns=["value"]
69 |
70 | return instrprice.iloc[:,0]
71 |
72 |
73 | def get_interest():
74 |
75 | authtoken='qWXuZcdwzwQ2GJQ88sNb'
76 | quandldef='FRED/INTDSRUSM193N'
77 | print quandldef
78 | data = Quandl.get(quandldef, authtoken=authtoken)
79 | data.columns=["value"]
80 | return data.iloc[:,0]/1200.0
81 |
82 |
83 |
84 | instrument_code="CORN"
85 | start_date=dict(GOLD=pd.datetime(1975,1,1), CORN=pd.datetime(1982,04,30), CRUDE_W=pd.datetime(1987,12,31))[instrument_code]
86 |
87 | data1=get_quandl(instrument_code)[start_date:]
88 |
89 | perc_data1= (data1 - data1.shift(1))/data1
90 |
91 | data2=get_adj_price(instrument_code).reindex(data1.index).ffill()
92 |
93 | perc_data2= (data2 - data2.shift(1))/data2
94 |
95 | irate=get_interest()
96 | irate=irate.reindex(perc_data2.index,method="ffill")
97 |
98 | perc_data2=perc_data2+irate
99 |
100 | data1=perc_data1+1.0
101 | data1=data1.cumprod()
102 |
103 | data2=perc_data2+1.0
104 | data2=data2.cumprod()
105 |
106 |
107 | #data2 = data2 - (data2.irow(0) - data1.irow(0))
108 | data3=data1-data2
109 | data=pd.concat([data1, data2], axis=1).ffill()
110 |
111 | data.columns=["Spot", "Future"]
112 | data.plot(style=["g-", "b--"])
113 | legend(loc="upper left")
114 |
115 | frame=plt.gca()
116 |
117 | #frame.get_yaxis().set_visible(False)
118 | #frame.set_ylim([0,50000])
119 |
120 |
121 |
122 | rcParams.update({'font.size': 18})
123 |
124 |
125 |
126 | def file_process(filename):
127 | fig = plt.gcf()
128 | fig.set_size_inches(18.5,10.5)
129 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
130 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
131 |
132 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
133 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
134 |
135 |
136 | file_process("contango_%s" % instrument_code)
137 |
138 | show()
139 |
140 |
141 | data3.plot()
142 | show()
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/plots_for_perhaps/realreturns.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | from random import random
4 |
5 | def pd_readcsv(filename, date_index_name="DATETIME"):
6 | """
7 | Reads a pandas data frame, with time index labelled
8 | package_name(/path1/path2.., filename
9 |
10 | :param filename: Filename with extension
11 | :type filename: str
12 |
13 | :param date_index_name: Column name of date index
14 | :type date_index_name: list of str
15 |
16 |
17 | :returns: pd.DataFrame
18 |
19 | """
20 |
21 | ans = pd.read_csv(filename)
22 | #ans.index = pd.to_datetime(ans[date_index_name]).values
23 | ans.index = ans[date_index_name].values
24 |
25 | del ans[date_index_name]
26 |
27 | ans.index.name = None
28 |
29 | return ans
30 |
31 | def geomean(x):
32 | if type(x) is pd.core.frame.DataFrame:
33 | return x.apply(geomean, 0)
34 | prod_returns=x+1.0
35 | cum_prod_returns=prod_returns.cumprod()
36 | final_pr=cum_prod_returns.values[-1]
37 | avg_pr=final_pr**(1/float(len(x.index)))
38 | return avg_pr-1.0
39 |
40 | def geostd(x):
41 | if type(x) is pd.core.frame.DataFrame:
42 | return x.apply(geostd, 0)
43 | gm=geomean(x)
44 |
45 | def _gsone(xitem, gm):
46 | return (np.log((1+xitem)/(1+gm)))**2.0
47 |
48 | items=[_gsone(xitem, gm) for xitem in x.values]
49 |
50 | return np.exp((np.mean(items))**.5)-1.0
51 |
52 |
53 | data=pd_readcsv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/USrealreturns.csv", "year")
54 |
55 | ## resampled version
56 | period_length=50
57 | monte_runs=1000
58 |
59 | eqbeatbonds=0
60 | eqbeatcash=0
61 | bobeatcash=0
62 | eqbeatport=0
63 |
64 | for monte in range(monte_runs):
65 | choose=[int(random()*len(data.index)) for notused in range(period_length)]
66 | subdata=data.iloc[choose,]
67 | subdata.index=pd.date_range(pd.datetime(1990,1,1), periods=period_length, freq="A")
68 | portfolio=subdata.SP500*.6+subdata.US10*.4
69 |
70 | gmeans=geomean(subdata)
71 | if gmeans.SP500>gmeans.US10:
72 | eqbeatbonds=eqbeatbonds+1
73 | if gmeans.SP500>gmeans.TBILL:
74 | eqbeatcash=eqbeatcash+1
75 | if gmeans.US10>gmeans.TBILL:
76 | bobeatcash=bobeatcash+1
77 |
78 | if gmeans.SP500>geomean(portfolio):
79 | eqbeatport=eqbeatport+1
80 |
81 | ## portfolio optimisation
82 | wts=[0.4, 0.6]
83 | wts=wts+[1-sum(wts)]
84 |
85 | portfolio=data.SP500*wts[0]+data.US10*wts[1]+data.TBILL*wts[2]
86 |
87 | print(geomean(portfolio))
88 | print(geostd(portfolio))
89 | print((geomean(portfolio) - 0.005)/geostd(portfolio))
90 |
91 |
--------------------------------------------------------------------------------
/plots_for_perhaps/regionalcorrplots.py:
--------------------------------------------------------------------------------
1 | from optimisation import *
2 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
3 | import matplotlib.pyplot as plt
4 | import Image
5 |
6 | from itertools import cycle
7 |
8 | from datetime import datetime as dt
9 |
10 | def file_process(filename):
11 | fig = plt.gcf()
12 | fig.set_size_inches(18.5,10.5)
13 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
14 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
15 |
16 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
17 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
18 |
19 |
20 |
21 | lines = ["-","--","-."]
22 | linecycler = cycle(lines)
23 | colorcycler=cycle(["red", "blue", "green"])
24 |
25 | def read_ts_csv(fname, dindex="Date"):
26 | data=pd.read_csv(fname)
27 | dateindex=[dt.strptime(dx, "%d/%m/%y") for dx in list(data[dindex])]
28 | data.index=dateindex
29 | del(data[dindex])
30 |
31 | return data
32 |
33 | def calc_asset_returns(rawdata, tickers):
34 | asset_returns=pd.concat([get_monthly_tr(tickname, rawdata) for tickname in tickers], axis=1)
35 | asset_returns.columns=tickers
36 |
37 | return asset_returns
38 |
39 | def get_monthly_tr(tickname, rawdata):
40 | total_returns=rawdata[tickname+"_TR"]
41 | return (total_returns / total_returns.shift(1)) - 1.0
42 |
43 | rawdata=read_ts_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/MSCI_data.csv")
44 |
45 | data=calc_asset_returns(rawdata, ["DEV_NORTHAM", "DEV_EUROPE", "DEV_ASIA"])
46 |
47 | def linehist(x, color="blue", linestyle="-", lw=1, passed_axis=None):
48 | y,binEdges =np.histogram(x)
49 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
50 |
51 | if passed_axis is None:
52 | plot(bincenters,y,'-', color=color, linestyle=linestyle, lw=lw)
53 | else:
54 | passed_axis.plot(bincenters,y,'-', color=color, linestyle=linestyle, lw=lw)
55 |
56 |
57 | """
58 | Plot bootstrapped statistics
59 |
60 | Awful code only works with 3 assets
61 | """
62 |
63 | """
64 | corrs=[]
65 |
66 | monte_carlo=1000
67 | monte_length=len(data)
68 |
69 | allcorrs=[]
70 | for unused_index in range(monte_carlo):
71 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
72 | returns=data.iloc[bs_idx,:]
73 | cm=returns.corr().values
74 | clist=[cm[0][1], cm[0][2],cm[1][2]]
75 | corrs.append(clist)
76 | allcorrs=allcorrs+clist
77 |
78 | codes=data.columns
79 | corrnames=["America / Europe", "America / Asia", "Europe / Asia", "All"]
80 |
81 | corrs=np.array(corrs)
82 |
83 | linehist(corrs[:,0], linestyle=next(linecycler), color=next(colorcycler))
84 | linehist(corrs[:,1], linestyle=next(linecycler), color=next(colorcycler))
85 | linehist(corrs[:,2], linestyle=next(linecycler), color=next(colorcycler))
86 | linehist(allcorrs, lw=3, color="black")
87 |
88 | allcorrs.sort()
89 | point75=allcorrs[int(len(allcorrs)*.75)]
90 | point50=np.median(allcorrs)
91 | plt.axvline(point75, linestyle="--", color="black")
92 | plt.axvline(point50, linestyle="--", color="black")
93 |
94 | legend(corrnames, loc="upper left")
95 |
96 | xlabel("Correlation")
97 |
98 |
99 | frame=plt.gca()
100 |
101 | frame.get_yaxis().set_visible(False)
102 | #frame.set_xticks([ -0.0, 0.25, 0.5])
103 |
104 |
105 |
106 | rcParams.update({'font.size': 18})
107 |
108 |
109 | file_process("corrregionals")
110 |
111 | show()
112 |
113 |
114 | data=calc_asset_returns(rawdata, ["EM_EMEA", "EM_LATAM", "EM_ASIA"])
115 |
116 | corrs=[]
117 |
118 | monte_carlo=1000
119 | monte_length=len(data)
120 |
121 | allcorrs=[]
122 | for unused_index in range(monte_carlo):
123 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
124 | returns=data.iloc[bs_idx,:]
125 | cm=returns.corr().values
126 | clist=[cm[0][1], cm[0][2],cm[1][2]]
127 | corrs.append(clist)
128 | allcorrs=allcorrs+clist
129 |
130 | codes=data.columns
131 | corrnames=["EMEA / Latam", "EMEA / Asia", "Latam / Asia"]
132 |
133 | corrs=np.array(corrs)
134 |
135 | linehist(corrs[:,0], linestyle=next(linecycler), color=next(colorcycler))
136 | linehist(corrs[:,1], linestyle=next(linecycler), color=next(colorcycler))
137 | linehist(corrs[:,2], linestyle=next(linecycler), color=next(colorcycler))
138 | linehist(allcorrs, lw=3, color="black")
139 |
140 | allcorrs.sort()
141 | point75=allcorrs[int(len(allcorrs)*.75)]
142 | point50=np.median(allcorrs)
143 | plt.axvline(point75, linestyle="--", color="black")
144 | plt.axvline(point50, linestyle="--", color="black")
145 |
146 | legend(corrnames, loc="upper left")
147 |
148 | xlabel("Correlation")
149 |
150 |
151 | frame=plt.gca()
152 |
153 | frame.get_yaxis().set_visible(False)
154 | #frame.set_xticks([ -0.0, 0.25, 0.5])
155 |
156 |
157 |
158 | rcParams.update({'font.size': 18})
159 |
160 | def file_process(filename):
161 | fig = plt.gcf()
162 | fig.set_size_inches(18.5,10.5)
163 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
164 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
165 |
166 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
167 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
168 |
169 | file_process("corrregionals")
170 |
171 | show()
172 |
173 |
174 | data=calc_asset_returns(rawdata, ["EM", "DEV"])
175 | corrs=[]
176 |
177 | monte_carlo=100
178 | monte_length=len(data)
179 |
180 | for unused_index in range(monte_carlo):
181 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
182 | returns=data.iloc[bs_idx,:]
183 | cm=returns.corr().values
184 | corrs.append(cm[0][1])
185 |
186 | corrs.sort()
187 | point75=corrs[int(len(corrs)*.75)]
188 |
189 |
190 |
191 | data=calc_asset_returns(rawdata, ["EM_EMEA", "EM_LATAM", "EM_ASIA", "DEV_EUROPE", "DEV_NORTHAM", "DEV_ASIA"])
192 |
193 | corrstack=[]
194 | corrs=[]
195 |
196 | monte_carlo=100
197 | monte_length=len(data)
198 |
199 | for unused_index in range(monte_carlo):
200 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
201 | returns=data.iloc[bs_idx,:]
202 | cm=returns.corr().values
203 | cm=np.tril(cm, k=-1)
204 | cm[cm==0]=np.NAN
205 | cx=[]
206 | for ci in cm:
207 | cx=cx+list(ci)
208 |
209 | clist=cx
210 | corrs.append(clist)
211 |
212 | corrs=np.array(corrs)
213 | corrs=corrs.transpose()
214 |
215 | for citem in corrs:
216 | if not all(np.isnan(citem)):
217 | corrstack.append(list(citem))
218 |
219 | allcorrs = sum(corrstack, [])
220 |
221 | fig, ax1 = plt.subplots()
222 |
223 | ax2 = ax1.twinx()
224 |
225 | linehist(allcorrs, lw=3, color="black", passed_axis=ax1)
226 |
227 |
228 | for cs in corrstack:
229 | linehist(cs, color="gray", passed_axis=ax2)
230 |
231 |
232 | allcorrs.sort()
233 | point75=allcorrs[int(len(allcorrs)*.75)]
234 | point50=np.median(allcorrs)
235 | plt.axvline(point75, linestyle="--", color="black")
236 | plt.axvline(point50, linestyle="--", color="black")
237 |
238 | show()
239 | """
240 |
241 |
242 |
243 | regions=dict(emea_dev=["UK", "IRELAND", "GERMANY", "AUSTRIA", "SWITZERLAND", "NETHERLANDS", "BELGIUM", "SWEDEN", "FINLAND",
244 | "NORWAY", "FRANCE", "ITALY", "SPAIN", "PORTUGAL", "ISRAEL"],
245 | asia_dev=["JAPAN", "AUSTRALIA", "NEW_ZEALAND", "HONG_KONG", "SINGAPORE"],
246 | america_dev=["USA", "CANADA"],
247 | latam_em=["BRAZIL", "MEXICO", "CHILE", "COLOMBIA", "PERU"],
248 | emea_em=[ "RUSSIA", "POLAND", "HUNGARY", "CZECH", "GREECE", "TURKEY", "QATAR",
249 | "UAE", "EGYPT", "SOUTH_AFRICA"],
250 | asia_em=["CHINA", "INDIA", "TAIWAN", "KOREA", "MALAYSIA", "INDONESIA", "THAILAND",
251 | "PHILLIPINES"])
252 |
253 | devlist=["emea_dev", "asia_dev", "america_dev"]
254 | emlist=["latam_em", "emea_em", "asia_em"]
255 |
256 | regionlist=emlist+devlist
257 |
258 | corrstack=[]
259 | for region in regionlist:
260 | codes=regions[region]
261 | data=calc_asset_returns(rawdata, codes)
262 | corrs=[]
263 |
264 | monte_carlo=100
265 | monte_length=len(data)
266 |
267 | for unused_index in range(monte_carlo):
268 | bs_idx=[int(random.uniform(0,1)*len(data)) for i in range(monte_length)]
269 | returns=data.iloc[bs_idx,:]
270 | cm=returns.corr().values
271 | cm=np.tril(cm, k=-1)
272 | cm[cm==0]=np.NAN
273 | cx=[]
274 | for ci in cm:
275 | cx=cx+list(ci)
276 |
277 | clist=cx
278 | corrs.append(clist)
279 |
280 | corrs=np.array(corrs)
281 | corrs=corrs.transpose()
282 |
283 | for citem in corrs:
284 | if not all(np.isnan(citem)):
285 | corrstack.append(list(citem))
286 |
287 |
288 | fig, ax1 = plt.subplots()
289 |
290 | ax2 = ax1.twinx()
291 |
292 |
293 |
294 |
295 | for cs in corrstack:
296 | linehist(cs, color="lightskyblue", passed_axis=ax2)
297 | linehist(corrstack[113], color="black", passed_axis=ax2)
298 |
299 | allcorrs = sum(corrstack, [])
300 |
301 | allcorrs.sort()
302 | point75=allcorrs[int(len(allcorrs)*.75)]
303 | point50=np.median(allcorrs)
304 | plt.axvline(point75, linestyle="--", color="black")
305 | plt.axvline(point50, linestyle="--", color="black")
306 |
307 | linehist(allcorrs, lw=4, color="black", passed_axis=ax1)
308 |
309 | frame=plt.gca()
310 |
311 | ax1.get_yaxis().set_visible(False)
312 | ax2.get_yaxis().set_visible(False)
313 | frame.set_xticks([ 0.0, 0.5])
314 | ax2.set_ylim([0.0, 70.0])
315 |
316 | rcParams.update({'font.size': 18})
317 |
318 | file_process("intraregioncorr")
319 |
320 | show()
321 |
--------------------------------------------------------------------------------
/plots_for_perhaps/relative_perf_uncertainty.py:
--------------------------------------------------------------------------------
1 |
2 | import Image
3 | from random import gauss
4 | import numpy as np
5 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, scatter
6 |
7 | import matplotlib.pylab as plt
8 | from itertools import cycle
9 | import pickle
10 | import pandas as pd
11 |
12 | lines = ["--","-","-."]
13 | linecycler = cycle(lines)
14 |
15 | def twocorrelatedseries(dindex, daily_mean, daily_mean2, daily_vol, corr):
16 |
17 | means = [daily_mean, daily_mean2]
18 | stds = [daily_vol]*2
19 | covs = [[stds[0]**2 , stds[0]*stds[1]*corr],
20 | [stds[0]*stds[1]*corr, stds[1]**2]]
21 |
22 | no_periods=len(dindex)
23 |
24 | m = np.random.multivariate_normal(means, covs, no_periods).T
25 |
26 | data1=pd.TimeSeries(m[0], dindex)
27 | data2=pd.TimeSeries(m[1], dindex)
28 | diff=12*(np.mean(data1)- np.mean(data2)) - 0.01 ## account for costs
29 |
30 | SR1=(12**.5)*data1.mean()/data1.std()
31 | SR2=(12**.5)*data2.mean()/data1.std()
32 | diffS=SR1 - SR2
33 |
34 | return (diff, diffS)
35 |
36 |
37 |
38 |
39 |
40 | ## path to difference for one thing no correlation
41 | annual_vol=0.20
42 | monthly_vol=annual_vol/12**.5
43 |
44 | annual_mean1=(annual_vol*.3)
45 | monthly_mean1=annual_mean1/12
46 |
47 | annual_mean2=annual_vol*.3
48 | monthly_mean2=annual_mean2/12
49 |
50 |
51 | ## a 10% one sided tests runs at around 1.28 standard deviations
52 | magic_number=1.28
53 |
54 | ## Make sure these match!
55 |
56 | monte_carlos=5000
57 |
58 |
59 |
60 | corrlist=[ 0.25, 0.5, 0.75, 0.8, 0.85, 0.9, 0.95]
61 | #corrlist=[-1.0, 0.0, 0.95]
62 |
63 | year_list=[1,2,3, 5,7, 10,20, 50]
64 |
65 | for year_length in year_list:
66 | for corr in corrlist:
67 |
68 | no_periods=year_length*12
69 | dindex=pd.date_range(pd.datetime(1980,1,1), periods=no_periods, freq="M")
70 |
71 | roll_list=[twocorrelatedseries(dindex, monthly_mean1, monthly_mean2, monthly_vol, corr=corr) for ii in range(monte_carlos)]
72 | means=[x[0] for x in roll_list]
73 | SR=[x[1] for x in roll_list]
74 |
75 | print "For %d years; correlation %.2f the SR figure is %.4f and mean figure is %.4f" % (year_length,
76 | corr, np.std(SR)*magic_number, np.std(means)*magic_number)
77 |
78 | mean_magic= np.std(means)*magic_number
79 | count=sum([x>mean_magic for x in means])
80 |
81 | print "Out of 100 managers %f were good" % (float(count)*(100.0/monte_carlos))
82 |
--------------------------------------------------------------------------------
/plots_for_perhaps/sectorreturns .csv:
--------------------------------------------------------------------------------
1 | Year,Consumer Disc.,Consumer Stap.,Energy,Financials,Health Care,Industrials,IT,Materials,Telecom,Utilities
2 | 2014,9.68%,16.03%,-7.82%,15.17%,25.34%,9.82%,20.11%,6.88%,3.00%,29.04%
3 | 2013,43.07%,26.14%,25.12%,35.58%,41.50%,40.67%,28.36%,25.59%,11.54%,13.17%
4 | 2012,21.87%,7.52%,2.28%,26.31%,15.18%,12.53%,13.10%,12.21%,12.52%,-2.90%
5 | 2011,4.38%,10.50%,2.82%,-18.37%,10.24%,-2.91%,1.27%,-11.62%,0.76%,14.78%
6 | 2010,25.70%,10.73%,17.93%,10.80%,0.71%,23.89%,9.12%,19.86%,12.31%,0.89%
7 | 2009,41.28%,14.92%,13.79%,17.23%,19.71%,20.89%,61.66%,48.58%,8.94%,11.93%
8 | 2008,-34.68%,-17.70%,-35.90%,-57.00%,-24.52%,-41.46%,-43.69%,-46.96%,-33.59%,-31.50%
9 | 2007,-14.30%,11.62%,32.43%,-20.81%,5.38%,9.79%,15.49%,20.04%,8.44%,15.77%
10 | 2006,17.20%,11.76%,22.24%,16.19%,5.84%,11.00%,7.73%,15.72%,32.06%,16.88%
11 | 2005,-7.40%,1.33%,29.09%,3.72%,4.88%,0.41%,0.41%,2.23%,-8.98%,12.82%
12 | 2004,13.18%,8.18%,31.51%,10.88%,1.73%,18.00%,2.64%,13.19%,19.89%,24.29%
13 | 2003,36.13%,9.23%,22.38%,27.92%,13.29%,29.71%,46.62%,34.78%,3.32%,21.06%
14 | 2002,-24.39%,-6.31%,-13.31%,-16.44%,-19.98%,-27.62%,-37.60%,-7.68%,-35.90%,-33.00%
15 | 2001,2.02%,-8.33%,-12.29%,-10.49%,-12.94%,-7.02%,-25.98%,0.98%,-13.70%,-32.52%
16 | 2000,-19.42%,15.03%,12.97%,24.40%,34.28%,5.16%,-40.87%,-17.60%,-39.41%,52.71%
17 | 1999,21.24%,-2.82%,16.92%,1.13%,2.86%,16.79%,98.27%,24.26%,32.09%,-10.99%
18 | 1998,44.21%,15.80%,-1.39%,11.87%,44.52%,12.03%,80.49%,-5.97%,49.02%,10.83%
19 | 1997,32.32%,30.50%,22.01%,45.44%,41.68%,24.99%,28.09%,6.31%,37.11%,18.37%
20 | 1996,10.47%,23.16%,21.68%,31.91%,18.81%,22.67%,43.26%,13.40%,-2.16%,0.23%
21 | 1995,18.29%,35.47%,25.53%,49.04%,53.32%,35.50%,37.18%,17.80%,35.84%,25.19%
22 | 1994,-10.74%,5.71%,-0.87%,-7.12%,9.12%,-5.28%,18.19%,3.02%,-9.82%,-18.64%
23 | 1993,12.23%,-7.58%,10.79%,7.83%,-11.83%,15.71%,20.23%,10.86%,9.90%,7.18%
24 | 1992,18.24%,2.82%,-0.39%,19.19%,-18.03%,7.29%,0.14%,8.13%,11.87%,1.30%
25 | 1991,41.48%,41.70%,6.88%,49.08%,53.67%,29.46%,9.14%,25.52%,13.17%,23.90%
26 | 1990,-17.43%,11.48%,-1.80%,-25.57%,12.47%,-12.30%,-3.24%,-15.92%,-18.79%,-8.14%
27 |
--------------------------------------------------------------------------------
/plots_for_perhaps/sectorreturns.csv:
--------------------------------------------------------------------------------
1 | Date,Consumer Disc.,Consumer Stap.,Energy,Financials,Health Care,Industrials,IT,Materials,Telecom,Utilities
2 | 01/01/90,-17.43,11.48,-1.80,-25.57,12.47,-12.30,-3.24,-15.92,-18.79,-8.14
3 | 01/01/91,41.48,41.70,6.88,49.08,53.67,29.46,9.14,25.52,13.17,23.90
4 | 01/01/92,18.24,2.82,-0.39,19.19,-18.03,7.29,0.14,8.13,11.87,1.30
5 | 01/01/93,12.23,-7.58,10.79,7.83,-11.83,15.71,20.23,10.86,9.90,7.18
6 | 01/01/94,-10.74,5.71,-0.87,-7.12,9.12,-5.28,18.19,3.02,-9.82,-18.64
7 | 01/01/95,18.29,35.47,25.53,49.04,53.32,35.50,37.18,17.80,35.84,25.19
8 | 01/01/96,10.47,23.16,21.68,31.91,18.81,22.67,43.26,13.40,-2.16,0.23
9 | 01/01/97,32.32,30.50,22.01,45.44,41.68,24.99,28.09,6.31,37.11,18.37
10 | 01/01/98,44.21,15.80,-1.39,11.87,44.52,12.03,80.49,-5.97,49.02,10.83
11 | 01/01/99,21.24,-2.82,16.92,1.13,2.86,16.79,98.27,24.26,32.09,-10.99
12 | 01/01/00,-19.42,15.03,12.97,24.40,34.28,5.16,-40.87,-17.60,-39.41,52.71
13 | 01/01/01,2.02,-8.33,-12.29,-10.49,-12.94,-7.02,-25.98,0.98,-13.70,-32.52
14 | 01/01/02,-24.39,-6.31,-13.31,-16.44,-19.98,-27.62,-37.60,-7.68,-35.90,-33.00
15 | 01/01/03,36.13,9.23,22.38,27.92,13.29,29.71,46.62,34.78,3.32,21.06
16 | 01/01/04,13.18,8.18,31.51,10.88,1.73,18.00,2.64,13.19,19.89,24.29
17 | 01/01/05,-7.40,1.33,29.09,3.72,4.88,0.41,0.41,2.23,-8.98,12.82
18 | 01/01/06,17.20,11.76,22.24,16.19,5.84,11.00,7.73,15.72,32.06,16.88
19 | 01/01/07,-14.30,11.62,32.43,-20.81,5.38,9.79,15.49,20.04,8.44,15.77
20 | 01/01/08,-34.68,-17.70,-35.90,-57.00,-24.52,-41.46,-43.69,-46.96,-33.59,-31.50
21 | 01/01/09,41.28,14.92,13.79,17.23,19.71,20.89,61.66,48.58,8.94,11.93
22 | 01/01/10,25.70,10.73,17.93,10.80,0.71,23.89,9.12,19.86,12.31,0.89
23 | 01/01/11,4.38,10.50,2.82,-18.37,10.24,-2.91,1.27,-11.62,0.76,14.78
24 | 01/01/12,21.87,7.52,2.28,26.31,15.18,12.53,13.10,12.21,12.52,-2.90
25 | 01/01/13,43.07,26.14,25.12,35.58,41.50,40.67,28.36,25.59,11.54,13.17
26 | 01/01/14,9.68,16.03,-7.82,15.17,25.34,9.82,20.11,6.88,3.00,29.04
27 |
--------------------------------------------------------------------------------
/plots_for_perhaps/smartbeta.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def variance_f(sigma, weights):
4 | return 1.0/((np.matrix(weights)*sigma*np.matrix(weights).transpose())[0,0]**.5)
5 |
6 | def make_corr(dimension, offdiag=0.0):
7 |
8 | corr=np.array([[offdiag]*dimension]*dimension)
9 | corr[np.diag_indices(dimension)]=1.0
10 | return corr
11 |
12 | power_law_set=[
13 | (0.55, -0.98),
14 | (0.5, -0.92),
15 | (0.4, -0.75),
16 | (0.3, -0.58),
17 | (0.2, -0.36)]
18 |
19 | universe_size=100
20 | power=power_law_set[0][1]
21 |
22 | start_cap_values=[((rank/100.0)**(power)) for rank in range(universe_size+1)[1:]]
23 | cfactor=0.80
24 | basestd=0.27
25 | basearithmean=0.05
26 | riskfree=0.0
27 |
28 | equalweight=False
29 |
30 | if equalweight:
31 | weights=[1.0/universe_size]*universe_size
32 | else:
33 | sumx=sum(start_cap_values)
34 | weights=[x/sumx for x in start_cap_values]
35 |
36 | def calc_stats(weights, basestd, basearithmean, corrmatrix=None, cfactor=1.0, cost=0.0, riskfree=0.0):
37 | if corrmatrix is None:
38 | universe_size=len(weights)
39 | corrmatrix=make_corr(universe_size, cfactor)
40 | div_factor=variance_f(corrmatrix, weights)
41 | new_std=basestd/div_factor
42 | variance=new_std**2
43 | gmm=basearithmean- variance/2.0
44 | gsr=(gmm - riskfree) / new_std
45 |
46 | cost_gmm = gmm - cost
47 | cost_gsr = (cost_gmm - riskfree) / new_std
48 |
49 | return (gmm, new_std, gsr, cost_gmm, cost_gsr)
50 |
51 | #print calc_stats(weights, basestd, basearithmean, cfactor=cfactor)
52 |
53 | ### Use TSX weights
54 | import pandas as pd
55 | tsx_weights=pd.read_csv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/XIU_holdings.csv")
56 | tsx_weights=tsx_weights.iloc[:60,]
57 | universe_size=60
58 |
59 |
60 |
61 | corrmatrix=np.zeros((universe_size, universe_size))
62 |
63 | for i in range(universe_size):
64 | for j in range(universe_size):
65 | if i==j:
66 | corr=1.0
67 | elif tsx_weights.irow(i).Sector==tsx_weights.irow(j).Sector:
68 | corr=0.85
69 | else:
70 | corr=0.75
71 | corrmatrix[i][j]=corr
72 |
73 | ## top down weights
74 | sectors=list(tsx_weights.Sector.unique())
75 | weight_each_sector=1.0/len(sectors)
76 |
77 | sector_counts=dict([(sector, sum(tsx_weights.Sector==sector)) for sector in sectors])
78 |
79 | topdown_weights=[]
80 | for (tidx, sector) in enumerate(tsx_weights.Sector):
81 | topdown_weights.append(weight_each_sector * (1.0/sector_counts[sector]))
82 |
83 | ##cap weights
84 | cap_weights=list(tsx_weights.Weight.values)
85 | cap_weights=cap_weights/sum(cap_weights)
86 |
87 |
88 |
89 |
90 | equal_weights=[1.0/universe_size]*universe_size
91 |
92 | # (gmm, new_std, gsr, cost_gmm, cost_gsr)
93 | print calc_stats(cap_weights, basestd, basearithmean, corrmatrix)
94 | print calc_stats(equal_weights, basestd, basearithmean, corrmatrix)
95 | print calc_stats(topdown_weights, basestd, basearithmean, corrmatrix)
96 |
97 |
98 |
99 | ## randomness
100 | ## only need to this one once
101 | random_topdown_weights =[]
102 | done_this_sector=[]
103 | for (tidx, sector) in enumerate(tsx_weights.Sector):
104 | if sector in done_this_sector:
105 | random_topdown_weights.append(0.0)
106 | else:
107 | random_topdown_weights.append(weight_each_sector)
108 | done_this_sector.append(sector)
109 |
110 | print calc_stats(random_topdown_weights, basestd, basearithmean, corrmatrix)
111 |
112 | psize=10
113 |
114 | import random
115 | ### multiple times
116 | all_gmeans=[]
117 | all_sr=[]
118 |
119 | for notused in range(10000):
120 | things=[int(np.ceil(random.uniform(0,61))) for notused2 in range(psize*2)]
121 | things=list(set(things)) ## make unique
122 | things=things[:psize] ##only need 10
123 | random_weights=[]
124 | for (tidx, sector) in enumerate(tsx_weights.Sector):
125 | if tidx in things:
126 | random_weights.append(1.0/psize)
127 | else:
128 | random_weights.append(0.0)
129 | stats = calc_stats(random_weights, basestd, basearithmean, corrmatrix)
130 | all_gmeans.append(stats[0])
131 | all_sr.append(stats[2])
132 |
133 | np.mean(all_gmeans)
134 | np.mean(all_sr)
135 |
--------------------------------------------------------------------------------
/plots_for_perhaps/stockselection.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | def variance_f(sigma, weights):
4 | return 1.0/((np.matrix(weights)*sigma*np.matrix(weights).transpose())[0,0]**.5)
5 |
6 | def make_corr(dimension, offdiag=0.0):
7 |
8 | corr=np.array([[offdiag]*dimension]*dimension)
9 | corr[np.diag_indices(dimension)]=1.0
10 | return corr
11 |
12 |
13 | def calc_stats(weights, basestd, basearithmean, corrmatrix=None, cfactor=1.0, cost=0.0, riskfree=0.0):
14 | if corrmatrix is None:
15 | universe_size=len(weights)
16 | corrmatrix=make_corr(universe_size, cfactor)
17 | div_factor=variance_f(corrmatrix, weights)
18 | new_std=basestd/div_factor
19 | variance=new_std**2
20 | gmm=basearithmean- variance/2.0
21 | gsr=(gmm - riskfree) / new_std
22 |
23 | cost_gmm = gmm - cost
24 | cost_gsr = (cost_gmm - riskfree) / new_std
25 |
26 | return (gmm, new_std, gsr, cost_gmm, cost_gsr)
27 |
28 |
29 | ### Use TSX weights
30 | import pandas as pd
31 | sector_count=11
32 | stocks_per_sector=2
33 | universe_size = sector_count * stocks_per_sector
34 |
35 | corrmatrix=np.zeros((universe_size, universe_size))
36 |
37 | insector=sum([range(sector_count)]*stocks_per_sector,[])
38 |
39 | for i in range(universe_size):
40 | for j in range(universe_size):
41 | if i==j:
42 | corr=1.0
43 | elif insector[i]==insector[j]:
44 | corr=0.85
45 | else:
46 | corr=0.75
47 | corrmatrix[i][j]=corr
48 |
49 | ## top down weights
50 | equal_weights=[1.0/universe_size]*universe_size
51 |
52 | # (gmm, new_std, gsr, cost_gmm, cost_gsr)
53 | basestd=.27
54 | basearithmean=.05
55 | print calc_stats(equal_weights, basestd, basearithmean, corrmatrix)
56 |
--------------------------------------------------------------------------------
/plots_for_perhaps/whichriskappetite.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | import numpy as np
3 | from random import random
4 |
5 | def pd_readcsv(filename, date_index_name="DATETIME"):
6 | """
7 | Reads a pandas data frame, with time index labelled
8 | package_name(/path1/path2.., filename
9 |
10 | :param filename: Filename with extension
11 | :type filename: str
12 |
13 | :param date_index_name: Column name of date index
14 | :type date_index_name: list of str
15 |
16 |
17 | :returns: pd.DataFrame
18 |
19 | """
20 |
21 | ans = pd.read_csv(filename)
22 | #ans.index = pd.to_datetime(ans[date_index_name]).values
23 | ans.index = ans[date_index_name].values
24 |
25 | del ans[date_index_name]
26 |
27 | ans.index.name = None
28 |
29 | return ans
30 |
31 | def geomean(x):
32 | if type(x) is pd.core.frame.DataFrame:
33 | return x.apply(geomean, 0)
34 | prod_returns=x+1.0
35 | cum_prod_returns=prod_returns.cumprod()
36 | final_pr=cum_prod_returns.values[-1]
37 | avg_pr=final_pr**(1/float(len(x.index)))
38 | return avg_pr-1.0
39 |
40 | def geostd(x):
41 | if type(x) is pd.core.frame.DataFrame:
42 | return x.apply(geostd, 0)
43 | gm=geomean(x)
44 |
45 | def _gsone(xitem, gm):
46 | return (np.log((1+xitem)/(1+gm)))**2.0
47 |
48 | items=[_gsone(xitem, gm) for xitem in x.values]
49 |
50 | return np.exp((np.mean(items))**.5)-1.0
51 |
52 |
53 | data=pd_readcsv("/home/rob/workspace/systematictradingexamples/plots_for_perhaps/USrealreturnsADJ.csv", "year")
54 |
55 | portfolios=dict([("All Equities", [0.0,1.0]),
56 | ("Maximum risk",[.2,.8]),
57 | ("Compromise",[.4,.6]),
58 | ("Maximum SR",[.68,.32])
59 | ])
60 |
61 | ## resampled version
62 | period_length=5
63 | monte_runs=100000
64 |
65 | results=dict([("All Equities", []),
66 | ("Maximum risk",[]),
67 | ("Compromise",[]),
68 | ("Maximum SR",[])
69 | ])
70 | for monte in range(monte_runs):
71 | choose=[int(random()*len(data.index)) for notused in range(period_length)]
72 | subdata=data.iloc[choose,]
73 | subdata.index=pd.date_range(pd.datetime(1990,1,1), periods=period_length, freq="A")
74 |
75 | for resname in results.keys():
76 | bond_weight=portfolios[resname][0]
77 | equity_weight=portfolios[resname][1]
78 | portfolio_returns=subdata.SP500*equity_weight+subdata.US10*bond_weight
79 |
80 | gmeans=geomean(portfolio_returns)
81 | results[resname].append(gmeans)
82 |
83 |
84 | from optimisation import *
85 | from matplotlib.pyplot import plot, show, xticks, xlabel, ylabel, legend, yscale, title, savefig, rcParams, figure, hist, text, bar, subplots
86 | import matplotlib.pyplot as plt
87 | import Image
88 |
89 | from itertools import cycle
90 |
91 | from datetime import datetime as dt
92 |
93 | def linehist(x, color="blue", linestyle="-"):
94 | y,binEdges =np.histogram(x, bins=50)
95 | bincenters = 0.5*(binEdges[1:]+binEdges[:-1])
96 | plot(bincenters,y,'-', color=color, linestyle=linestyle)
97 |
98 | return list(y),list(bincenters)
99 |
100 | def revperc(x, a):
101 |
102 | return float(len([xx for xx in x if xx<=a]))/float(len(x))
103 |
104 | def file_process(filename):
105 | fig = plt.gcf()
106 | fig.set_size_inches(18.5,10.5)
107 | fig.savefig("/home/rob/%s.png" % filename,dpi=300)
108 | fig.savefig("/home/rob/%sLOWRES.png" % filename,dpi=50)
109 |
110 | Image.open("/home/rob/%s.png" % filename).convert('L').save("/home/rob/%s.jpg" % filename)
111 | Image.open("/home/rob/%sLOWRES.png" % filename).convert('L').save("/home/rob/%sLOWRES.jpg" % filename)
112 |
113 | def plot_vertical_line(x, y, binEdges, linestyles="dashed", colors="k"):
114 | plt.vlines(x, 0, np.interp(x, binEdges, y), linestyles=linestyles, colors=colors)
115 |
116 | resname="All Equities"
117 | ax1=plt.subplot(221)
118 | y,binEdges=linehist(results[resname])
119 |
120 | plt.setp(ax1.get_xticklabels(), visible=False)
121 | print resname
122 | print np.median(results[resname])
123 | print np.mean(results[resname])
124 | print np.percentile( results[resname],5)
125 | print revperc(results[resname], 0.0)
126 | title(resname)
127 | plot_vertical_line(np.median(results[resname]), y, binEdges, linestyles="dashed")
128 | plot_vertical_line(0.0, y, binEdges, linestyles="solid", colors="gray")
129 |
130 | frame=plt.gca()
131 |
132 |
133 | frame.get_yaxis().set_visible(False)
134 |
135 |
136 | resname="Maximum risk"
137 | ax2=plt.subplot(223, sharex=ax1, sharey=ax1)
138 | y,binEdges=linehist(results[resname])
139 | plt.setp(ax2.get_xticklabels(), fontsize=18)
140 | print resname
141 | print np.median(results[resname])
142 | print np.percentile(results[resname],5)
143 | print revperc(results[resname], 0.0)
144 | title(resname)
145 | plot_vertical_line(np.median(results[resname]), y, binEdges, linestyles="dashed")
146 | plot_vertical_line(0.0, y, binEdges, linestyles="solid", colors="gray")
147 |
148 | frame=plt.gca()
149 |
150 | frame.get_yaxis().set_visible(False)
151 |
152 |
153 | resname="Compromise"
154 | ax3=plt.subplot(222, sharex=ax1, sharey=ax1)
155 | y,binEdges=linehist(results[resname])
156 |
157 | plt.setp(ax3.get_xticklabels(), visible=False)
158 | print resname
159 | print np.median(results[resname])
160 | print np.percentile(results[resname], 5)
161 | print revperc(results[resname], 0.0)
162 | title(resname)
163 | plot_vertical_line(np.median(results[resname]), y, binEdges, linestyles="dashed")
164 | plot_vertical_line(0.0, y, binEdges, linestyles="solid", colors="gray")
165 |
166 | frame=plt.gca()
167 |
168 | frame.get_yaxis().set_visible(False)
169 |
170 |
171 | resname="Maximum SR"
172 | ax4=plt.subplot(224, sharex=ax1, sharey=ax1)
173 | y,binEdges=linehist(results[resname])
174 |
175 | title(resname)
176 |
177 | plt.setp(ax4.get_xticklabels(), fontsize=18)
178 | print resname
179 | print np.median(results[resname])
180 | print np.percentile(results[resname],5 )
181 | print revperc(results[resname], 0.0)
182 | plot_vertical_line(np.median(results[resname]), y, binEdges, linestyles="dashed")
183 | plot_vertical_line(0.0, y, binEdges, linestyles="solid", colors="gray")
184 |
185 |
186 | frame=plt.gca()
187 |
188 | frame.get_yaxis().set_visible(False)
189 | #frame.set_xticks([-0.05, 0.0, 0.05, 0.10, 0.15])
190 | #frame.set_xlim([-0.1, 0.2])
191 |
192 | frame.set_xticks([ -.1, 0.0, 0.10, 0.2, ])
193 | frame.set_xlim([-0.2, 0.3])
194 |
195 |
196 | rcParams.update({'font.size': 18})
197 |
198 | file_process("riskappetite%d" % period_length)
199 |
200 | show()
201 |
--------------------------------------------------------------------------------
/randomadvanced.py:
--------------------------------------------------------------------------------
1 | from commonrandom import autocorr_skewed_returns, adj_moments_for_rho
2 | from common import arbitrary_timeseries, autocorr, ROOT_DAYS_IN_YEAR
3 | import matplotlib.pyplot as plt
4 | import numpy as np
5 | import scipy.stats as st
6 |
7 | """
8 | This code verifies that the single frequency method for generating returns with a given distribution
9 | skew, autocorrelation; all works...
10 |
11 | """
12 |
13 | monte_runs=1000
14 |
15 | for want_rho in np.arange(-0.8, 0.8, 0.1):
16 | for want_sharpe in [0.0, 0.5, 1.0, 2.0]:
17 | for want_stdev in [1.0]:
18 | for want_skew in [-2.0, -1.0, 0.0, 0.5, 1.0]:
19 |
20 | want_mean=want_stdev*want_sharpe/16.0
21 |
22 | ## autocorrelation introduces biases
23 |
24 | (adj_want_mean, adj_want_skew, adj_want_stdev)=adj_moments_for_rho(want_rho, want_mean, want_skew, want_stdev)
25 |
26 | manyans=[autocorr_skewed_returns(want_rho, adj_want_mean, adj_want_stdev, adj_want_skew) for notused in range(monte_runs)]
27 | sample_mean=np.mean([np.mean(x) for x in manyans])
28 | sample_std=np.mean([np.std(x) for x in manyans])
29 | sample_skew=np.mean([st.skew(x) for x in manyans])
30 | sample_rho=np.mean([autocorr(x) for x in manyans])
31 |
32 | print "****************************"
33 | print "Mean, wanted %.2f got %.6f" % (want_mean, sample_mean)
34 | print "Stdev, wanted %.2f got %.4f" % (want_stdev, sample_std)
35 | print "Skew, wanted %.2f got %.4f" % (want_skew, sample_skew)
36 | print "Rho, wanted %.2f got %.4f" % (want_rho, sample_rho)
37 |
38 |
39 |
--------------------------------------------------------------------------------
/randomanalysisofequitycurveproperties.py:
--------------------------------------------------------------------------------
1 | """
2 | See http://qoppac.blogspot.co.uk/2015/11/using-random-data.html for more examples
3 | """
4 |
5 | from commonrandom import arbitrary_timeindex, skew_returns_annualised
6 | from matplotlib.pyplot import show, hist
7 | from common import DAYS_IN_YEAR, ROOT_DAYS_IN_YEAR, account_curve
8 | import pandas as pd
9 | import numpy as np
10 | import scipy.stats as st
11 |
12 | def plot_random_curves(pddf_rand_data):
13 |
14 | pddf_rand_data.cumsum().plot(colormap="Pastel2", legend=False)
15 |
16 | avg_random=pddf_rand_data.cumsum().mean(axis=1)
17 | avg_random.plot(color="k")
18 |
19 |
20 |
21 |
22 | """
23 | Set up the parameters
24 | """
25 |
26 | length_backtest_years=10
27 | number_of_random_curves=1000
28 | annualSR=0.5
29 | want_skew=1.0
30 |
31 |
32 | """
33 | Do the bootstrap of many random curves
34 | """
35 |
36 | length_bdays=length_backtest_years*DAYS_IN_YEAR
37 |
38 | random_curves=[skew_returns_annualised(annualSR=annualSR, want_skew=want_skew, size=length_bdays)
39 | for NotUsed in range(number_of_random_curves)]
40 |
41 | ## Turn into a dataframe
42 |
43 | random_curves_npa=np.array(random_curves).transpose()
44 | pddf_rand_data=pd.DataFrame(random_curves_npa, index=arbitrary_timeindex(length_bdays), columns=[str(i) for i in range(number_of_random_curves)])
45 |
46 | ## This is a nice representation as well
47 | acccurves_rand_data=[account_curve(pddf_rand_data[x]) for x in pddf_rand_data]
48 |
49 | """
50 | Plot the curves, just for interest
51 | """
52 |
53 | plot_random_curves(pddf_rand_data)
54 | show()
55 |
56 | """
57 | Get the statistic we are interested in
58 |
59 | Change 'function_to_apply' to get what you want
60 |
61 | """
62 |
63 | function_to_apply=np.percentile
64 | function_args=(100.0/DAYS_IN_YEAR,)
65 | results=pddf_rand_data.apply(function_to_apply, axis=0, args=function_args)
66 |
67 | ## Annualise (not always needed, depends on the statistic)
68 | #results=[x*ROOT_DAYS_IN_YEAR for x in results]
69 |
70 |
71 | hist(results, 100)
72 | show()
73 |
74 |
75 | """
76 | Or we can do this kind of thing
77 | """
78 |
79 | results=[x.worst_drawdown() for x in acccurves_rand_data]
80 | hist(results, 100)
81 | show()
82 |
--------------------------------------------------------------------------------
/randomexplorationofportfolioallocation.py:
--------------------------------------------------------------------------------
1 | """
2 | Explore portfolio optimisation
3 | """
4 | import random
5 | import matplotlib.pyplot as plt
6 | from commonrandom import threeassetportfolio
7 | from optimisation import opt_shrinkage, bootstrap_portfolio, handcrafted
8 | import numpy as np
9 | from common import DAYS_IN_YEAR, ROOT_DAYS_IN_YEAR
10 | import pandas as pd
11 |
12 | ## Generate random data with various characteristics
13 | ## each tuple is sharpes, then correlations
14 | portfolio=([ .5, .5, .5],[0.8, 0.0, 0.0])
15 |
16 | years_to_test=[1, 5, 10, 20]
17 |
18 | monte_length=200
19 | test_period_years=1
20 | ## arbitrary
21 | methods=["HC", "BS", "S00", "S03", "S30","S33","S06" ,"S60","S36","S63", "S66","S01", "S10", "S31", "S61","S13", "S16", "S11"]
22 |
23 | ## used for plot order
24 | methods_ms=[ "S00", "S03", "S06", "S01", "S30", "S33", "S36", "S31", "S60", "S63", "S66", "S61", "S10", "S13", "S16", "S11", "HC", "BS"]
25 |
26 | methods_by_cs=[ "S00", "S30", "S60", "S10", "S03", "S33", "S63", "S13", "S06", "S36" ,"S66", "S16", "S01", "S31", "S61", "S11", "HC", "BS"]
27 |
28 | benchmarket="S00"
29 | sfactor_lookup=dict([("1",1.0), ("0", 0.0), ("3", 0.333), ("6", 0.66) ])
30 |
31 | ## For the shorter periods we just use earlier examples of data we've already tested
32 | max_length_years=np.max(years_to_test)+1
33 | plength=max_length_years*DAYS_IN_YEAR+100
34 | test_period_days=test_period_years*DAYS_IN_YEAR
35 |
36 | SRlist, clist=portfolio
37 | all_returns=[threeassetportfolio(plength=plength, SRlist=SRlist, clist=clist) for notused in range(monte_length)]
38 |
39 |
40 | for data_length in years_to_test:
41 | print "Testing data length %d" % data_length
42 | in_sample_start=0
43 | in_sample_end=data_length*DAYS_IN_YEAR
44 | out_sample_start=in_sample_end+1
45 | out_sample_end=out_sample_start+test_period_days
46 |
47 |
48 | answers_this_port_SR=[np.nan]
49 | answers_this_port_degrade=[np.nan]
50 |
51 | returns_to_use_insample=[one_return_series.iloc[in_sample_start:in_sample_end] for one_return_series in all_returns]
52 | returns_to_use_outsample=[one_return_series.iloc[out_sample_start:out_sample_end] for one_return_series in all_returns]
53 |
54 | len_returns_to_use_os=[one_returns.shape[0] for one_returns in returns_to_use_outsample]
55 | len_returns_to_use_is=[one_returns.shape[0] for one_returns in returns_to_use_insample]
56 |
57 |
58 | """
59 | Run each method over
60 | """
61 |
62 |
63 | for method_name in methods:
64 |
65 | if method_name=="HC":
66 | wts=[handcrafted(one_returns) for one_returns in returns_to_use_insample]
67 | elif method_name=="BS":
68 | wts=[bootstrap_portfolio(one_returns, monte_carlo=50, monte_length=int(data_length*DAYS_IN_YEAR*.1), equalisemeans=False, equalisevols=False)
69 | for one_returns in returns_to_use_insample]
70 | elif method_name[0]=="S":
71 | ## shrinkage
72 | shrinkage_factors=(sfactor_lookup[method_name[1]], sfactor_lookup[method_name[2]])
73 | wts=[opt_shrinkage(one_returns, shrinkage_factors, equalisevols=False) for one_returns in returns_to_use_insample]
74 | else:
75 | raise Exception("Not recognised")
76 |
77 | ## See how these weights did in and out of sample
78 | wts_mat_os=[np.array([list(weights)]*length_needed, ndmin=2) for (weights, length_needed) in zip( wts, len_returns_to_use_os)]
79 | wts_mat_is=[np.array([list(weights)]*length_needed, ndmin=2) for (weights, length_needed) in zip( wts, len_returns_to_use_is)]
80 | perf_os=[weight_mat*returns.values for (weight_mat, returns) in zip(wts_mat_os, returns_to_use_outsample)]
81 | perf_is=[weight_mat*returns.values for (weight_mat, returns) in zip(wts_mat_is, returns_to_use_insample)]
82 |
83 | perf_os=[perf.sum(axis=1) for perf in perf_os]
84 | perf_is=[perf.sum(axis=1) for perf in perf_is]
85 |
86 | ann_return_os=[ROOT_DAYS_IN_YEAR*perf.mean() for perf in perf_os]
87 | sharpe_ann_return_os=np.mean(ann_return_os)/np.std(ann_return_os)
88 |
89 | sharpe_os=[ROOT_DAYS_IN_YEAR*perf.mean()/perf.std() for perf in perf_os]
90 | sharpe_is=[ROOT_DAYS_IN_YEAR*perf.mean()/perf.std() for perf in perf_is]
91 |
92 | degradation=[s_os - s_is for (s_os, s_is) in zip(sharpe_os, sharpe_is)]
93 | avg_degradation=np.mean(degradation)
94 |
95 | ## Stack up the results
96 |
97 | answers_this_port_SR.append(sharpe_ann_return_os)
98 | answers_this_port_degrade.append(avg_degradation)
99 |
100 |
101 | results=pd.DataFrame(np.array([answers_this_port_SR, answers_this_port_degrade]).transpose(), columns=["SR", "Degradation"], index=[""]+methods)
102 |
103 | for (methods_idx, mtitle) in zip([methods_ms, methods_by_cs], ["mean shrinkage", "corr shrinkage"]):
104 |
105 | results=results.loc[[""]+methods_idx]
106 |
107 | ax=results.plot(use_index=False)
108 | plt.xticks(range(len(methods_idx)+1))
109 | plt.title("SR out of sample; Degradation out vs in sample; fit over %d years, order %s" % (data_length, mtitle))
110 | ax.set_xticklabels([""]+methods_idx)
111 | plt.show()
112 |
113 |
--------------------------------------------------------------------------------
/randomportfolioexample.py:
--------------------------------------------------------------------------------
1 | """
2 | See http://qoppac.blogspot.co.uk/2015/11/using-random-data.html for more examples
3 | """
4 |
5 |
6 | from matplotlib.pyplot import show
7 | from commonrandom import threeassetportfolio
8 |
9 | SRlist=[.5, 1.0, 0.0]
10 | clist=[.9,.5,-0.5]
11 |
12 | ans=threeassetportfolio(plength=5000, SRlist=SRlist, clist=clist)
13 | ans.cumsum().plot()
14 | show()
15 |
16 | print "Standard deviation"
17 |
18 | print ans.std()
19 |
20 | print "Correlation"
21 | print ans.corr()
22 |
23 | print "Mean"
24 | print ans.mean()
25 |
26 | print "Annualised SR"
27 |
28 | print 16*ans.mean()/ans.std()
--------------------------------------------------------------------------------
/randompriceexample.py:
--------------------------------------------------------------------------------
1 | """
2 | See http://qoppac.blogspot.co.uk/2015/11/using-random-data.html for more examples
3 | """
4 |
5 |
6 | from common import arbitrary_timeseries
7 | from commonrandom import generate_trendy_price
8 | from matplotlib.pyplot import show
9 |
10 |
11 | ans=arbitrary_timeseries(generate_trendy_price(Nlength=180, Tlength=30, Xamplitude=10.0, Volscale=0.0))
12 | print ans
13 | print len(ans)
14 | ans.plot()
15 | show()
16 |
17 | ans=arbitrary_timeseries(generate_trendy_price(Nlength=180, Tlength=30, Xamplitude=10.0, Volscale=0.1))
18 | ans.plot()
19 | show()
20 |
21 | ans=arbitrary_timeseries(generate_trendy_price(Nlength=180, Tlength=30, Xamplitude=10.0, Volscale=0.5))
22 | ans.plot()
23 | show()
24 |
--------------------------------------------------------------------------------
/randomskewreturnsexample.py:
--------------------------------------------------------------------------------
1 | """
2 | See http://qoppac.blogspot.co.uk/2015/11/using-random-data.html for more examples
3 | """
4 |
5 |
6 | from commonrandom import arbitrary_timeseries, skew_returns_annualised
7 | from common import cum_perc
8 | from matplotlib.pyplot import show
9 |
10 | ans=arbitrary_timeseries(skew_returns_annualised(annualSR=0.5, want_skew=0.0, size=2500))
11 | cum_perc(ans).plot()
12 | show()
13 |
14 | ans=arbitrary_timeseries(skew_returns_annualised(annualSR=0.5, want_skew=1.0, size=2500))
15 | cum_perc(ans).plot()
16 | show()
17 |
18 | ans=arbitrary_timeseries(skew_returns_annualised(annualSR=1.0, want_skew=-3.0, size=2500))
19 | cum_perc(ans).plot()
20 | show()
21 |
22 |
--------------------------------------------------------------------------------
/randomtestequitycurvetrading.py:
--------------------------------------------------------------------------------
1 | """
2 | Test equity curve trading
3 | """
4 |
5 | import numpy as np
6 | from commonrandom import autocorr_skewed_returns, adj_moments_for_rho
7 | from matplotlib.pyplot import plot, title, show, xticks, legend
8 | from common import account_curve, arbitrary_timeseries
9 | import pandas as pd
10 |
11 | NO_OVERLAY=1000
12 |
13 |
14 |
15 | ## Use period_length=1, peiod_length=5 for weeks, 21 for months
16 | ## Choose scenario type - "VaryingSharpeRatio", "VaryingSkew", "VaryingAuto"
17 | scenario_type="VaryingAutoMore"
18 | period_length=1
19 |
20 | annualised_costs_SR=0.03
21 |
22 | ## Higher number improves accuracy of results
23 | monte_runs=100
24 | curve_length_years=20
25 |
26 |
27 | def divxx(x):
28 | if x[1]==0.0:
29 | return np.nan
30 |
31 | return x[0]/-x[1]
32 |
33 | def isbelow(cum_x, mav_x, idx):
34 | ## returns 1 if cum_x>mav_x at idx, 0 otherwise
35 | if cum_x[idx]>=mav_x[idx]:
36 | return 1.0
37 | return 0.0
38 |
39 | def apply_overlay(x, N_length, period_stdev, costs_SR=0):
40 | """
41 | apply an equity curve filter overlay
42 |
43 | x is a pd time series of returns
44 |
45 | N_length is the mav to apply
46 |
47 | Returns a new x with 'flat spots'
48 |
49 | """
50 | if N_length==NO_OVERLAY:
51 | return x
52 |
53 | cum_x=x.cumsum()
54 | mav_x=pd.rolling_mean(cum_x, N_length)
55 | filter_x=pd.TimeSeries([isbelow(cum_x, mav_x, idx) for idx in range(len(x))], x.index)
56 |
57 | ## can only apply with a lag (!)
58 | filtered_x=x*filter_x.shift(1)
59 |
60 | if costs_SR>0:
61 | ## apply costs
62 | ## first work out the turnover
63 | ## note everything is adjusted for the period we are in
64 |
65 | turnover=filter_x.diff().abs().mean()/2.0
66 |
67 | return_degrade=costs_SR*turnover
68 | filtered_x = filtered_x - return_degrade
69 |
70 | return filtered_x
71 |
72 | def plot_array(data_list, array_title, N_windows, scenario_list):
73 |
74 | data_array=np.array(data_list).transpose()
75 |
76 | plot(data_array)
77 | xticks(range(data_array.shape[0]), N_windows)
78 | legend([x[0] for x in scenario_list])
79 |
80 | title(array_title)
81 | show()
82 |
83 |
84 |
85 |
86 | if scenario_type=="VaryingSharpeRatio":
87 | ##fixed characteristics
88 |
89 | skew_list=[0.0]
90 | autocorr_list=[0.0]
91 |
92 | ## varying characteristics
93 | ann_sharpe_list=[-1.0, 0.0, 1.0, 2.0]
94 |
95 | elif scenario_type=="VaryingSkew":
96 | autocorr_list=[0.0]
97 | ann_sharpe_list=[1.0]
98 |
99 | ## varying characteristics
100 | skew_list=[-2.0, 0.0, 1.0]
101 |
102 |
103 | elif scenario_type=="VaryingAuto":
104 | skew_list=[0.0]
105 | ann_sharpe_list=[1.0]
106 |
107 | ## varying characteristics
108 | autocorr_list=[-0.3, 0.0, 0.3]
109 |
110 | elif scenario_type=="VaryingAutoMore":
111 | skew_list=[0.0]
112 | ann_sharpe_list=[1.0]
113 |
114 | ## varying characteristics
115 | autocorr_list=[-0.3, 0.0, 0.1, 0.2, 0.3]
116 |
117 |
118 |
119 | else:
120 | raise Exception("%s?" % scenario_type)
121 |
122 | ## Calculations...
123 | ## Fixed
124 | ann_stdev=0.20
125 | min_N_size=3
126 | curve_length=curve_length_years*256/period_length
127 |
128 | N_windows=[10,25,40, 64, 128, 256, 512]
129 |
130 | root_period_adj=(256.0/period_length)**.5
131 | period_adj=256.0/period_length
132 | period_stdev=ann_stdev/root_period_adj
133 | N_windows=[int(np.ceil(x/period_length)) for x in N_windows]
134 | N_windows=[x for x in N_windows if x>(min_N_size-1)]
135 | period_mean=[ann_stdev*x/period_adj for x in ann_sharpe_list]
136 |
137 | costs_SR=annualised_costs_SR/root_period_adj
138 |
139 |
140 | ## Each scenario is a tuple: 'name', mean return, stdev returns, skew, autocorrelation
141 | if scenario_type=="VaryingSharpeRatio":
142 | scenario_list=[("SR:%.1f" % ann_sharpe_list[idx], period_mean[idx], period_stdev, skew_list[0], autocorr_list[0]) for idx in range(len(ann_sharpe_list))]
143 |
144 | elif scenario_type=="VaryingSkew":
145 | scenario_list=[("Skew:%.1f" % skew_list[idx], period_mean[0], period_stdev, skew_list[idx], autocorr_list[0]) for idx in range(len(skew_list))]
146 |
147 | elif scenario_type=="VaryingAuto" or scenario_type=="VaryingAutoMore":
148 | scenario_list=[("Rho:%.1f" % autocorr_list[idx], period_mean[0], period_stdev, skew_list[0], autocorr_list[idx]) for idx in range(len(autocorr_list))]
149 |
150 | else:
151 | raise Exception("%s?" % scenario_type)
152 |
153 | N_windows=N_windows+[NO_OVERLAY]
154 |
155 | """
156 | These lists are where we stack up the stuff we are plotting
157 | """
158 | avg_annual_returns=[]
159 | avg_drawdowns=[]
160 | max_drawdowns=[]
161 | avg_avg_ratio=[]
162 | avg_max_ratio=[]
163 |
164 | for scenario in scenario_list:
165 | print "Scenario:"
166 | print scenario
167 | (notUsed, want_mean, want_stdev, want_skew, want_rho)=scenario
168 | (adj_want_mean, adj_want_skew, adj_want_stdev)=adj_moments_for_rho(want_rho, want_mean, want_skew, want_stdev)
169 |
170 | avg_annual_returns_this_scenario=[]
171 | avg_drawdowns_this_scenario=[]
172 | max_drawdowns_this_scenario=[]
173 | avg_avg_ratio_this_scenario=[]
174 | avg_max_ratio_this_scenario=[]
175 |
176 | ## Window '1000' is no ovelay at all
177 | for N_length in N_windows:
178 | print "N_length: %d" % N_length
179 | ## Generate a lot of random data
180 | many_curves_raw=[arbitrary_timeseries(autocorr_skewed_returns(want_rho, adj_want_mean, adj_want_stdev, adj_want_skew, size=curve_length)) for notused in range(monte_runs)]
181 |
182 | ## Apply the overlay
183 | many_curves=[account_curve(apply_overlay(x, N_length, period_stdev, costs_SR)) for x in many_curves_raw]
184 |
185 | ## Calc statistics
186 | annual_returns_these_curves=[np.mean(x)*period_adj for x in many_curves]
187 | avg_drawdowns_these_curves=[x.avg_drawdown() for x in many_curves]
188 | max_drawdowns_these_curves=[x.worst_drawdown() for x in many_curves]
189 |
190 | ## Add results
191 | avg_annual_returns_this_scenario.append(np.mean(annual_returns_these_curves))
192 | avg_drawdowns_this_scenario.append(np.nanmean(avg_drawdowns_these_curves))
193 | max_drawdowns_this_scenario.append(np.nanmean(max_drawdowns_these_curves))
194 | avg_avg_ratio_this_scenario.append(np.nanmean([divxx(x) for x in zip(annual_returns_these_curves, avg_drawdowns_these_curves)]))
195 | avg_max_ratio_this_scenario.append(np.nanmean([divxx(x) for x in zip(annual_returns_these_curves, max_drawdowns_these_curves)]))
196 |
197 | avg_annual_returns.append(avg_annual_returns_this_scenario)
198 | avg_drawdowns.append(avg_drawdowns_this_scenario)
199 | max_drawdowns.append(max_drawdowns_this_scenario)
200 | avg_avg_ratio.append(avg_avg_ratio_this_scenario)
201 | avg_max_ratio.append(avg_max_ratio_this_scenario)
202 |
203 |
204 |
205 | plot_array(avg_annual_returns, "Average annual return", N_windows, scenario_list)
206 | plot_array(avg_drawdowns, "Average drawdowns", N_windows, scenario_list)
207 | plot_array(max_drawdowns, "Max drawdowns", N_windows, scenario_list)
208 | plot_array(avg_avg_ratio, "Average return / Average drawdown", N_windows, scenario_list)
209 | plot_array(avg_max_ratio, "Average return / Max drawdown", N_windows, scenario_list)
210 |
--------------------------------------------------------------------------------
/tradingrules.py:
--------------------------------------------------------------------------------
1 | """
2 | Trading rules
3 |
4 | Note that the scripted versions of these can be found in the carry and ewmac files
5 |
6 | """
7 |
8 | import pandas as pd
9 | import numpy as np
10 | from common import cap_series, pd_readcsv, find_datediff, ROOT_DAYS_IN_YEAR, ewmac_forecast_scalar
11 |
12 | def calc_carry_forecast(carrydata, price, f_scalar=30.0):
13 |
14 | """
15 | Carry calculation
16 |
17 | Formulation here will work whether we are trading the nearest contract or not
18 |
19 | For other asset classes you will have to work out nerpu (net expected return in price units) yourself
20 | """
21 |
22 | nerpu=carrydata.apply(find_datediff, axis=1)
23 |
24 | stdev_returns=volatility(price)
25 | ann_stdev=stdev_returns*ROOT_DAYS_IN_YEAR
26 |
27 | raw_carry=nerpu/ann_stdev
28 |
29 | forecast=raw_carry*f_scalar
30 |
31 | cap_forecast=cap_series(forecast)
32 |
33 | return cap_forecast
34 |
35 | def volatility(price, vol_lookback=25):
36 | return pd.ewmstd(price - price.shift(1), span=vol_lookback, min_periods=vol_lookback)
37 |
38 |
39 | def calc_ewmac_forecast(price, Lfast, Lslow=None, usescalar=True):
40 |
41 |
42 | """
43 | Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback
44 |
45 | Assumes that 'price' is daily data
46 | """
47 | ## price: This is the stitched price series
48 | ## We can't use the price of the contract we're trading, or the volatility will be jumpy
49 | ## And we'll miss out on the rolldown. See http://qoppac.blogspot.co.uk/2015/05/systems-building-futures-rolling.html
50 |
51 | if Lslow is None:
52 | Lslow=4*Lfast
53 |
54 | ## We don't need to calculate the decay parameter, just use the span directly
55 |
56 | fast_ewma=pd.ewma(price, span=Lfast)
57 | slow_ewma=pd.ewma(price, span=Lslow)
58 | raw_ewmac=fast_ewma - slow_ewma
59 |
60 | ## volatility adjustment
61 | stdev_returns=volatility(price)
62 | vol_adj_ewmac=raw_ewmac/stdev_returns
63 |
64 | ## scaling adjustment
65 | if usescalar:
66 | f_scalar=ewmac_forecast_scalar(Lfast, Lslow)
67 | forecast=vol_adj_ewmac*f_scalar
68 | else:
69 | forecast=vol_adj_ewmac
70 |
71 | cap_forecast=cap_series(forecast, capmin=-20.0,capmax=20.0)
72 |
73 | return cap_forecast
--------------------------------------------------------------------------------
/whichcurvewouldyoupick.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Generate two account curves, one better than the other over the long run
4 |
5 | When we see one we like for demonstration purposes we 'snapshot' it
6 |
7 | We then re-generate the plot year by year
8 |
9 |
10 | """
11 |
12 | from common import ROOT_DAYS_IN_YEAR, DAYS_IN_YEAR, arbitrary_timeseries, slices_for_ts
13 | from commonrandom import autocorr_skewed_returns
14 | from matplotlib.pyplot import show
15 | import pandas as pd
16 |
17 | ## set the problem up
18 | rho=0.0
19 | want_skew=0.0
20 | want_Sharpes=[0.5, 1.0]
21 | length_period_years=20
22 |
23 | # calculations
24 | length_period=length_period_years*DAYS_IN_YEAR
25 | want_ann_stdev=0.2
26 | want_ann_mean=[ann_SR*want_ann_stdev for ann_SR in want_Sharpes]
27 | want_mean_list=[this_ann_mean/DAYS_IN_YEAR for this_ann_mean in want_ann_mean]
28 | want_stdev=want_ann_stdev/ROOT_DAYS_IN_YEAR
29 |
30 | happywithcurve=False
31 |
32 | while not happywithcurve:
33 | ## generate a new Curve
34 | equitycurves=[arbitrary_timeseries(autocorr_skewed_returns(rho, want_mean, want_stdev,
35 | want_skew, size=length_period), index_start=pd.datetime(1995,1,1))
36 | for want_mean in want_mean_list]
37 | equitycurves_pd=pd.concat(equitycurves, axis=1)
38 | equitycurves_pd.columns=["A", "B"]
39 | equitycurves_pd.cumsum().plot()
40 | show()
41 | ans=raw_input("Happy with this? Y / N? ")
42 | if ans=="Y":
43 | happywithcurve=True
44 |
45 | sliced_data=slices_for_ts(equitycurves[0])
46 | start_point=sliced_data[0]
47 |
48 | for idx in range(len(sliced_data))[1:]:
49 | sliced_curves=[eq_curve[start_point:sliced_data[idx]] for eq_curve in equitycurves]
50 | equitycurves_pd=pd.concat(sliced_curves, axis=1)
51 | equitycurves_pd.columns=["A", "B"]
52 | equitycurves_pd.cumsum().plot()
53 | show()
54 |
55 |
--------------------------------------------------------------------------------