├── .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 | --------------------------------------------------------------------------------