ml-finance-python
python scripts for finance machine learning
git clone https://9o.is/git/ml-finance-python.git
notebook.ipynb
(13993B)
1 {
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "metadata": {
6 "deletable": true,
7 "editable": true
8 },
9 "source": [
10 "#Exercise Answer Key: Position Concentration Risk\n",
11 "\n",
12 "## Lecture Link\n",
13 "\n",
14 "This exercise notebook refers to this lecture. Please use the lecture for explanations and sample code.\n",
15 "\n",
16 "https://www.quantopian.com/lectures#Position-Concentration-Risk\n",
17 "\n",
18 "Part of the Quantopian Lecture Series:\n",
19 "\n",
20 "* [www.quantopian.com/lectures](https://www.quantopian.com/lectures)\n",
21 "* [github.com/quantopian/research_public](https://github.com/quantopian/research_public)"
22 ]
23 },
24 {
25 "cell_type": "code",
26 "execution_count": null,
27 "metadata": {
28 "collapsed": false,
29 "deletable": true,
30 "editable": true
31 },
32 "outputs": [],
33 "source": [
34 "import numpy as np\n",
35 "import pandas as pd\n",
36 "import scipy.stats as stats\n",
37 "import matplotlib.pyplot as plt\n",
38 "import math\n",
39 "import cvxpy"
40 ]
41 },
42 {
43 "cell_type": "markdown",
44 "metadata": {
45 "deletable": true,
46 "editable": true
47 },
48 "source": [
49 "##Helper Functions"
50 ]
51 },
52 {
53 "cell_type": "code",
54 "execution_count": null,
55 "metadata": {
56 "collapsed": true,
57 "deletable": true,
58 "editable": true
59 },
60 "outputs": [],
61 "source": [
62 "def get_markowitz_weights(mu, Sigma, gamma=1, max_position=1.0, max_leverage=1.0, short=False):\n",
63 " w = cvxpy.Variable(len(Sigma))\n",
64 " g = cvxpy.Parameter(sign='positive')\n",
65 " L = cvxpy.Parameter()\n",
66 " g.value = gamma\n",
67 " L.value = max_leverage\n",
68 " try:\n",
69 " ret = mu.T*w\n",
70 " except ValueError:\n",
71 " ret = mu*w\n",
72 " \n",
73 " risk = cvxpy.quad_form(w, Sigma)\n",
74 " \n",
75 " objective = cvxpy.Maximize(ret - g*risk)\n",
76 " constraints = [\n",
77 " cvxpy.abs(w) < max_position,\n",
78 " cvxpy.norm(w, 1) <= L, # Make it so we don't have to invest everything\n",
79 " ]\n",
80 " \n",
81 " if not short:\n",
82 " constraints.append(w >= 0) # Force all positive weights\n",
83 " \n",
84 " prob = cvxpy.Problem(\n",
85 " objective,\n",
86 " constraints\n",
87 " )\n",
88 " \n",
89 " result = prob.solve()\n",
90 " \n",
91 " return w.value"
92 ]
93 },
94 {
95 "cell_type": "markdown",
96 "metadata": {
97 "deletable": true,
98 "editable": true
99 },
100 "source": [
101 "#Exercise 1: Roulette Simulation\n",
102 "\n",
103 "A roulette table has 38 pockets: 1 through 36, 0, and 00. A bet on an even number pays out at a ratio of 1:1. Landing on 0 and 00 count as losing.\n",
104 "\n",
105 "You have $100 and are betting on an even number.\n",
106 "\n",
107 "##a. All In\n",
108 "\n",
109 "By running 1000 simulations, find the mean and standard deviation of the payout if you bet your entire $100 on one round. "
110 ]
111 },
112 {
113 "cell_type": "code",
114 "execution_count": null,
115 "metadata": {
116 "collapsed": false,
117 "deletable": true,
118 "editable": true,
119 "scrolled": false
120 },
121 "outputs": [],
122 "source": [
123 "universes = 1000\n",
124 "evens = 19\n",
125 "total = 38\n",
126 "payout = 100\n",
127 "rounds = 1\n",
128 "results = np.zeros(universes)\n",
129 "\n",
130 "#Your code goes here"
131 ]
132 },
133 {
134 "cell_type": "markdown",
135 "metadata": {
136 "deletable": true,
137 "editable": true
138 },
139 "source": [
140 "##b. $1 Bets\n",
141 "\n",
142 "By running 1000 simulations, find the mean and standard deviation of the payout if instead you bet $1 at a time and play 100 rounds."
143 ]
144 },
145 {
146 "cell_type": "code",
147 "execution_count": null,
148 "metadata": {
149 "collapsed": false,
150 "deletable": true,
151 "editable": true
152 },
153 "outputs": [],
154 "source": [
155 "universes = 1000\n",
156 "evens = 19\n",
157 "total = 38\n",
158 "payout = 1\n",
159 "rounds = 100\n",
160 "results = np.zeros(universes)\n",
161 "\n",
162 "#Your code goes here"
163 ]
164 },
165 {
166 "cell_type": "markdown",
167 "metadata": {
168 "deletable": true,
169 "editable": true
170 },
171 "source": [
172 "#Exercise 2: Portfolio Diversification\n",
173 "\n",
174 "##a. Single Asset\n",
175 "\n",
176 "Use the pricing data below to find the standard deviation of the returns of `AMZN` in the second half of the year 2015 and plot the price against time."
177 ]
178 },
179 {
180 "cell_type": "code",
181 "execution_count": null,
182 "metadata": {
183 "collapsed": false,
184 "deletable": true,
185 "editable": true
186 },
187 "outputs": [],
188 "source": [
189 "time_start = '2015-01-01'\n",
190 "time_halfway = '2015-07-01'\n",
191 "time_end = '2016-01-01'\n",
192 "AMZN_r = get_pricing('AMZN', fields='price', start_date=time_start, end_date=time_end).pct_change()[1:]\n",
193 "X = np.linspace(0, len(AMZN_r), len(AMZN_r))\n",
194 "\n",
195 "#Your code goes here"
196 ]
197 },
198 {
199 "cell_type": "markdown",
200 "metadata": {
201 "deletable": true,
202 "editable": true
203 },
204 "source": [
205 "##b. Equally Weighted Portfolio\n",
206 "\n",
207 "Create an equally weighted portfolio of the following 10 stocks, find the standard deviation of the portfolio's returns, and then plot the returns for the second half of 2015 along with the `AMZN` returns from above. Putting AMZN in a portfolio of 19 other securities should diversify the idiosyncratic risk and lower the price variability.\n",
208 "\n",
209 "Hint: To calculate weighted returns dot the weight matrix `eweights_df` with the splice of the returns matrix containing the `symbol_list` pricing data (`returns_df[symbol_list]`)."
210 ]
211 },
212 {
213 "cell_type": "code",
214 "execution_count": null,
215 "metadata": {
216 "collapsed": false,
217 "deletable": true,
218 "editable": true
219 },
220 "outputs": [],
221 "source": [
222 "symbol_list = ['BEN', 'SYMC', 'IP', 'SWKS', 'IVZ', 'MJN', 'WMB', 'LB', 'TWX', 'NFX', 'PFE', 'LLY', 'HP', 'JPM', 'CXO', 'TJX', 'CAG', 'BBT', 'ATVI', 'NFLX']\n",
223 "prices_df = get_pricing(symbol_list, fields=['price']\n",
224 " , start_date=time_start, end_date=time_end)['price']\n",
225 "prices_df.columns = map(lambda x: x.symbol, prices_df.columns)\n",
226 "\n",
227 "eweights_df = len(symbol_list) * [float(1)/len(symbol_list)]\n",
228 "\n",
229 "returns_df = prices_df.pct_change(1)[1:]\n",
230 "\n",
231 "#Your code goes here"
232 ]
233 },
234 {
235 "cell_type": "markdown",
236 "metadata": {
237 "deletable": true,
238 "editable": true
239 },
240 "source": [
241 "##c. Market Weighted Portfolio\n",
242 "\n",
243 "Create a new portfolio of the same assets, this time weighted by market capitalization, find the standard deviation of the portfolio returns, and then plot the portfolio returns along with both results from above. Weighting using market capitalization brings us closer to the theoretical efficient portfolio, a portfolio of investments containing every single asset on the market, each weighted proportionately to its presence in the market. \n",
244 "\n",
245 "The market cap is found using a pipeline factor, the steps for which are below."
246 ]
247 },
248 {
249 "cell_type": "code",
250 "execution_count": null,
251 "metadata": {
252 "collapsed": false,
253 "deletable": true,
254 "editable": true
255 },
256 "outputs": [],
257 "source": [
258 "#Pipeline Setup\n",
259 "from quantopian.research import run_pipeline\n",
260 "from quantopian.pipeline import Pipeline\n",
261 "from quantopian.pipeline.data import morningstar \n",
262 "from quantopian.pipeline.factors import CustomFactor\n",
263 "from quantopian.pipeline.classifiers.morningstar import Sector\n",
264 "from quantopian.pipeline.filters import QTradableStocksUS\n",
265 "from time import time\n",
266 "\n",
267 "universe = QTradableStocksUS()\n",
268 "\n",
269 "pipe = Pipeline(columns = {'Market Cap' : morningstar.valuation.market_cap.latest},\n",
270 " screen=universe\n",
271 ")\n",
272 "\n",
273 "start_timer = time()\n",
274 "results = run_pipeline(pipe, time_start, time_end)\n",
275 "end_timer = time()\n",
276 "results.fillna(value=0);\n",
277 "\n",
278 "print \"Time to run pipeline %.2f secs\" % (end_timer - start_timer)\n",
279 "\n",
280 "# This is important as sometimes the first data returned won't be on the specified start date\n",
281 "first_trading_day = results.index.levels[0][1]\n",
282 "\n",
283 "market_cap = results.loc[first_trading_day]['Market Cap']\n",
284 "\n",
285 "market_cap.index = [x.symbol for x in market_cap.index]#pd.MultiIndex.from_tuples([(x[0], x[1].symbol) for x in market_cap.index])\n",
286 "\n",
287 "mcs = market_cap # pd.DataFrame(market_cap.loc[(first_trading_day,)].loc[symbol_list]).transpose()"
288 ]
289 },
290 {
291 "cell_type": "code",
292 "execution_count": null,
293 "metadata": {
294 "collapsed": false,
295 "deletable": true,
296 "editable": true,
297 "scrolled": false
298 },
299 "outputs": [],
300 "source": [
301 "mweights = (mcs[symbol_list]/sum(mcs[symbol_list])).transpose()\n",
302 " \n",
303 "#Your code goes here"
304 ]
305 },
306 {
307 "cell_type": "markdown",
308 "metadata": {
309 "deletable": true,
310 "editable": true
311 },
312 "source": [
313 "##d. Markowitz Portfolio\n",
314 "\n",
315 "Create a new portfolio of the same assets, this time using the `get_markowitz_weights` helper function to create the Markowitz mean-variance portfolio. Use the pricing data from the first half of 2015 to calibrate the weights, and then plot the portfolio returns for the second half of 2015.\n",
316 "\n",
317 "### Important Note\n",
318 "\n",
319 "If the weights from the lookback window (6 prior months), are correlated with the weights of the forward window (6 following months), then this optimization should be helpful in reducing out portfolio volatility going forward. However, this is often not the case in real life. Real markets are complicated, and historical volatility may not be a good predictor of future volatility. Volatility forecasting models are an entire area of research in finance, so don't think that just because historic volatility of your portfolio was low, it will be equally low in the future. This is just one technique that attempts to control portfolio risk, there is a more complete discussion of this in this lecture:\n",
320 "\n",
321 "https://www.quantopian.com/lectures/risk-constrained-portfolio-optimization"
322 ]
323 },
324 {
325 "cell_type": "code",
326 "execution_count": null,
327 "metadata": {
328 "collapsed": false,
329 "deletable": true,
330 "editable": true,
331 "scrolled": false
332 },
333 "outputs": [],
334 "source": [
335 "mu = returns_df[symbol_list].\\\n",
336 " loc[:time_halfway].fillna(0).mean().as_matrix()\n",
337 "sigma = returns_df[symbol_list].\\\n",
338 " loc[:time_halfway].fillna(0).cov().as_matrix()\n",
339 "\n",
340 "mkweights_df = get_markowitz_weights(mu, sigma)\n",
341 "\n",
342 "#Your code goes here"
343 ]
344 },
345 {
346 "cell_type": "markdown",
347 "metadata": {
348 "deletable": true,
349 "editable": true
350 },
351 "source": [
352 "Although the Markowitz portfolio was supposed to produce the portfolio with the least variance for the given returns, it failed to do so in this out-of-sample scenario.\n",
353 "\n",
354 "As discussed above, covariance matrices are volatile and tend to shift rapidly. When we calibrated the Markowitz weights we used the covariance matrix from the first half of 2015 as our sample and ran it through the second half of 2016, meaning our test was out-of-sample. Due to the volatile nature of covariance matrices, it is likely that the covariance matrix had significantly changed between the sample and out-of-sample time periods, making our Markowitz weights invalid for the out-of-sample period."
355 ]
356 },
357 {
358 "cell_type": "markdown",
359 "metadata": {
360 "deletable": true,
361 "editable": true
362 },
363 "source": [
364 "---\n",
365 "\n",
366 "Congratulations on completing the Position Concentration Risk exercises!\n",
367 "\n",
368 "As you learn more about writing trading algorithms and the Quantopian platform, be sure to check out the daily [Quantopian Contest](https://www.quantopian.com/contest), in which you can compete for a cash prize every day.\n",
369 "\n",
370 "Start by going through the [Writing a Contest Algorithm](https://www.quantopian.com/tutorials/contest) tutorial."
371 ]
372 },
373 {
374 "cell_type": "markdown",
375 "metadata": {
376 "deletable": true,
377 "editable": true
378 },
379 "source": [
380 "*This presentation is for informational purposes only and does not constitute an offer to sell, a solic\n",
381 "itation to buy, or a recommendation for any security; nor does it constitute an offer to provide investment advisory or other services by Quantopian, Inc. (\"Quantopian\"). Nothing contained herein constitutes investment advice or offers any opinion with respect to the suitability of any security, and any views expressed herein should not be taken as advice to buy, sell, or hold any security or as an endorsement of any security or company. In preparing the information contained herein, Quantopian, Inc. has not taken into account the investment needs, objectives, and financial circumstances of any particular investor. Any views expressed and data illustrated herein were prepared based upon information, believed to be reliable, available to Quantopian, Inc. at the time of publication. Quantopian makes no guarantees as to their accuracy or completeness. All information is subject to change and may quickly become unreliable for various reasons, including changes in market conditions or economic circumstances.*"
382 ]
383 }
384 ],
385 "metadata": {
386 "kernelspec": {
387 "display_name": "Python 2",
388 "language": "python",
389 "name": "python2"
390 },
391 "language_info": {
392 "codemirror_mode": {
393 "name": "ipython",
394 "version": 2
395 },
396 "file_extension": ".py",
397 "mimetype": "text/x-python",
398 "name": "python",
399 "nbconvert_exporter": "python",
400 "pygments_lexer": "ipython2",
401 "version": "2.7.12"
402 }
403 },
404 "nbformat": 4,
405 "nbformat_minor": 0
406 }