aboutsummaryrefslogtreecommitdiffstats
path: root/python/notebooks
diff options
context:
space:
mode:
Diffstat (limited to 'python/notebooks')
-rw-r--r--python/notebooks/Asset allocation.ipynb222
-rw-r--r--python/notebooks/Portfolio analysis.ipynb101
-rw-r--r--python/notebooks/tranches numbers.ipynb323
3 files changed, 646 insertions, 0 deletions
diff --git a/python/notebooks/Asset allocation.ipynb b/python/notebooks/Asset allocation.ipynb
new file mode 100644
index 00000000..ac122400
--- /dev/null
+++ b/python/notebooks/Asset allocation.ipynb
@@ -0,0 +1,222 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ipywidgets import interact, interactive, fixed, FloatSlider\n",
+ "from IPython.display import display\n",
+ "import ipywidgets as widgets\n",
+ "import numpy as np\n",
+ "import math\n",
+ "import cvxpy\n",
+ "from inspect import signature\n",
+ "%matplotlib inline"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We model the assets in a 1-factor model setting. Namely conditionally on the factor $F$, the assets\n",
+ "are independent, and $r_i$, the return of asset $i$ is given by:\n",
+ "$$r_i = \\mu_i + \\delta_i(F-\\bar F) + \\sigma_i \\varepsilon_i$$\n",
+ "with $\\forall i,\\,\\textrm{Cov}(F, \\varepsilon_i)=0$, and $\\varepsilon\\sim\\mathcal{N}(0, \\mathbf{1})$ \n",
+ "\n",
+ "We can show that:\n",
+ "$$\\textrm{Corr}(r_i, F)=\\rho_i = \\frac{\\delta_i \\sqrt{\\textrm{Var}(F)}}{\\sqrt{\\textrm{Var}(r_i)}}$$\n",
+ "and with $\\textrm{Var(r_i)}=\\delta_i^2\\textrm{Var}(F) + \\sigma_i^2$, we get:\n",
+ "$$\\rho_i = \\frac{1}{\\sqrt{1+\\frac{\\sigma_i^2}{\\delta_i^2\\textrm{Var(F)}}}}$$\n",
+ "$$\\forall i\\neq j,\\;\\textrm{Cov}(r_i, r_j)=\\delta_i\\delta_j\\textrm{Var}(F)$$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def cor2cov(Rho, vol):\n",
+ " return np.diag(vol) @ Rho @ np.diag(vol)\n",
+ "\n",
+ "def rho(sigma, delta, volF):\n",
+ " \"\"\" computes the correlation between the asset and the factor \"\"\"\n",
+ " return 1/math.sqrt(1+sigma**2/(delta**2*volF**2))\n",
+ "\n",
+ "def resid_vol(rho, delta, volF):\n",
+ " \"\"\" computes the residual of the asset \"\"\"\n",
+ " return math.sqrt(delta**2*volF**2*(1/rho**2-1))\n",
+ "\n",
+ "def var(rho, delta, volF):\n",
+ " \"\"\" computes the variance of the asset \"\"\"\n",
+ " return delta**2*volF**2+resid_vol(rho, delta, volF)**2\n",
+ "\n",
+ "volHY = 0.07\n",
+ "## Edwin's parameters\n",
+ "rho = {'CLO': 0.6,\n",
+ " 'CSO': 0.5,\n",
+ " 'Subprime': 0.3}\n",
+ "delta = {'CLO': 0.4,\n",
+ " 'CSO': 0.2,\n",
+ " 'Subprime': 0.6}\n",
+ "mu = np.array([0.01, 0.075, 0.065, 0.25])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from matplotlib import pyplot as plt\n",
+ "plt.style.use('ggplot')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def compute_allocation(rho_clo = 0.9, rho_cso=0.6, rho_subprime=0.2,\n",
+ " delta_clo=1.2, delta_cso=0.4, delta_subprime=0.8,\n",
+ " mu_hy=0.02, mu_clo=0.07, mu_cso=0.08, mu_subprime=0.25):\n",
+ " rho = {'CLO': rho_clo,\n",
+ " 'CSO': rho_cso,\n",
+ " 'Subprime': rho_subprime}\n",
+ " delta = {'CLO': delta_clo,\n",
+ " 'CSO': delta_cso,\n",
+ " 'Subprime': delta_subprime}\n",
+ " assets = ['CLO', 'CSO', 'Subprime']\n",
+ " mu = np.array([mu_hy, mu_clo, mu_cso, mu_subprime])\n",
+ " u = volHY * np.array([delta[a] for a in assets])\n",
+ " Sigma = np.outer(u, u) + np.diag([resid_vol(rho[a], delta[a], volHY)**2\n",
+ " for a in assets])\n",
+ " v = volHY**2 * np.array([1] + [delta[a] for a in assets])\n",
+ " Sigma = np.vstack((v, np.c_[v[1:], Sigma]))\n",
+ "\n",
+ " sharpe = mu/np.sqrt(np.diag(Sigma))\n",
+ "\n",
+ " gamma = cvxpy.Parameter(sign='positive')\n",
+ " w = cvxpy.Variable(4)\n",
+ " ret = mu.T*w\n",
+ " risk = cvxpy.quad_form(w, Sigma)\n",
+ " prob = cvxpy.Problem(cvxpy.Maximize(ret-gamma*risk),\n",
+ " [cvxpy.sum_entries(w[1:]) - 0.1*w[0] == 1,\n",
+ " w[1:] >= 0,\n",
+ " w[0] <= 0])\n",
+ "\n",
+ " gamma_x = np.linspace(0, 20, 500)\n",
+ " W = np.empty((4, gamma_x.size))\n",
+ " for i, val in enumerate(gamma_x):\n",
+ " gamma.value = val\n",
+ " prob.solve()\n",
+ " W[:,i] = np.asarray(w.value).squeeze()\n",
+ " fund_return = mu@W\n",
+ " fund_vol = np.array([math.sqrt(W[:,i]@Sigma@W[:,i]) for i in range(gamma_x.size)])\n",
+ " return (W, fund_return, fund_vol)\n",
+ "\n",
+ "def plot_allocation(plot_vol=True, **kwargs):\n",
+ " W, fund_return, fund_vol = compute_allocation(**kwargs)\n",
+ " gamma_x = np.linspace(0, 20, 500)\n",
+ " fig, ax1 = plt.subplots()\n",
+ " ax1.stackplot(gamma_x, W[1:,], labels=['CLO', 'CSO', 'Subprime'])\n",
+ " ax1.set_xlabel('risk factor')\n",
+ " ax1.set_ylabel('portfolio weights')\n",
+ " ax1.legend()\n",
+ " # ax1.text(0.3, 0.82, 'RMBS')\n",
+ " # ax1.text(0.5, 0.45, 'CSO')\n",
+ " # ax1.text(0.5, 0.15, 'CLO')\n",
+ " ax1.set_ylim([0, 1])\n",
+ " ax2 = ax1.twinx()\n",
+ " if plot_vol:\n",
+ " ax2.plot(gamma_x, fund_vol, lw=1, color=\"grey\")\n",
+ " ax2.set_ylabel('fund volatility')\n",
+ " else:\n",
+ " ax2.plot(gamma_x, fund_return, lw=1, color=\"grey\")\n",
+ " ax2.set_ylabel('fund return')\n",
+ " plt.show()\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def desc(greek, asset):\n",
+ " \"\"\"returns the latex label\"\"\"\n",
+ " return r'${0}_{{\\textrm{{{1}}}}}$'.format('\\\\'+greek, asset)\n",
+ "\n",
+ "desc_mapping = {}\n",
+ "assets = ['CLO', 'CSO', 'Subprime']\n",
+ "for greek in ['rho', 'delta', 'mu']:\n",
+ " if greek == 'mu':\n",
+ " assets.append('HY')\n",
+ " desc_mapping.update({desc(greek, a): '{0}_{1}'.format(greek, a.lower()) for a in assets})\n",
+ " \n",
+ "w = interactive(plot_allocation, rho_clo=FloatSlider(description=desc('rho', 'CLO'), min=0., max=1., step=0.1, value=0.9),\n",
+ " rho_cso=FloatSlider(description=desc('rho', 'CSO'), min=0., max=1., step=0.1, value=0.6),\n",
+ " rho_subprime=FloatSlider(description=desc('rho', 'Subprime'), min=0., max=1., step=0.1, value=0.2),\n",
+ " delta_clo=FloatSlider(description=desc('delta', 'CLO'), min=0., max=2., step=0.1, value=1.2),\n",
+ " delta_cso=FloatSlider(description=desc('delta', 'CSO'), min=0., max=2., step=0.1, value=0.4),\n",
+ " delta_subprime=FloatSlider(description=desc('delta', 'Subprime'), min=0., max=2., step=0.1, value=0.8),\n",
+ " mu_hy = FloatSlider(description=desc('mu', 'HY'), min=0., max=.1, step=0.01, value=0.02),\n",
+ " mu_clo = FloatSlider(description=desc('mu', 'CLO'), min=0., max=.12, step=0.01, value=0.07),\n",
+ " mu_cso = FloatSlider(description=desc('mu', 'CSO'), min=0., max=.12, step=0.01, value=0.08),\n",
+ " mu_subprime = FloatSlider(description=desc('mu', 'Subprime'), min=0.1, max=.35, step=0.01, value=0.25))\n",
+ "\n",
+ "## black magic to get back to the function order\n",
+ "s = signature(compute_allocation)\n",
+ "param_ordering = {p:i for i, p in enumerate(s.parameters)}\n",
+ "param_ordering[None] = -1\n",
+ "w_ordered = sorted([c for c in w.children], key=lambda x: param_ordering[desc_mapping.get(x.description)])\n",
+ "display(*w_ordered)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "args = w.kwargs.copy()\n",
+ "args.pop('plot_vol')\n",
+ "W, fund_return, fund_vol = compute_allocation(**args)\n",
+ "import pandas as pd\n",
+ "df = pd.DataFrame(np.c_[W.T, fund_return, fund_vol], columns=['HY', 'CLO', 'CSO', 'Subprime', 'return', 'vol'])\n",
+ "df.to_csv('weights.csv', index=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.5.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/python/notebooks/Portfolio analysis.ipynb b/python/notebooks/Portfolio analysis.ipynb
new file mode 100644
index 00000000..ce0395b3
--- /dev/null
+++ b/python/notebooks/Portfolio analysis.ipynb
@@ -0,0 +1,101 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "cd ~/projects/code/python"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from analytics import Portfolio, BlackSwaption, Index\n",
+ "import datetime\n",
+ "option_delta = Index.from_tradeid(874)\n",
+ "option1 = BlackSwaption.from_tradeid(7, option_delta)\n",
+ "option2 = BlackSwaption.from_tradeid(8, option_delta)\n",
+ "portf = Portfolio([option_delta, option1, option2])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "option1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "option2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "portf"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "portf.trade_date = datetime.date.today()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "portf.mark()\n",
+ "portf"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/python/notebooks/tranches numbers.ipynb b/python/notebooks/tranches numbers.ipynb
new file mode 100644
index 00000000..71cb44f8
--- /dev/null
+++ b/python/notebooks/tranches numbers.ipynb
@@ -0,0 +1,323 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%matplotlib\n",
+ "import seaborn\n",
+ "seaborn.mpl.rcParams['figure.figsize'] = (12.0, 8.0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "from ipywidgets import interact\n",
+ "from collections import OrderedDict"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "SQL_CON = \"postgresql://serenitas_user@debian/serenitasdb\"\n",
+ "runs = (pd.\n",
+ " read_sql(\"SELECT DISTINCT index, series, tenor from risk_numbers ORDER BY index, series\", SQL_CON).\n",
+ " itertuples(index=False, name='run'))\n",
+ "runs = OrderedDict([(\"%s %s %s\" % (r.index, r.series, r.tenor), (r.index, r.series, r.tenor)) for r in runs])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df = pd.read_sql(\"SELECT * FROM risk_numbers\", SQL_CON, index_col=['date', 'index', 'series', 'tenor'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "risk_numbers = ['skew', 'Dealer Deltas', 'Model Deltas', 'Forward Deltas', 'gammas', 'durations', 'thetas']\n",
+ "@interact(index=runs, what=risk_numbers)\n",
+ "def corrplot(index, what):\n",
+ " selection = df.xs(index, level=[1,2,3], drop_level=True)\n",
+ " cols = selection.attach.iloc[0]\n",
+ " cols = [\"{}-{}\".format(a,d) for a, d in zip(cols[:-2], cols[1:-1])]\n",
+ " selection = selection[what].apply(pd.Series)\n",
+ " selection.drop(selection.columns[-1], axis=1, inplace=True)\n",
+ " selection.columns = cols\n",
+ " selection.plot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_tranche_quotes(index, series, tenor):\n",
+ " df = pd.read_sql(\"SELECT * FROM tranche_quotes WHERE index=%s and series=%s and tenor =%s\",\n",
+ " SQL_CON, params=(index, series, tenor), index_col='quotedate')\n",
+ " df.sort_index(inplace=True)\n",
+ " return df\n",
+ "def get_index_quotes(index, series, tenor):\n",
+ " df = pd.read_sql(\"SELECT * FROM index_quotes WHERE index=%s and series=%s and tenor=%s\",\n",
+ " SQL_CON, params = (index, series, tenor), index_col=['date'], parse_dates=['date'])\n",
+ " df.sort_index(inplace=True)\n",
+ " return df\n",
+ "ig25_tranches = get_tranche_quotes('IG', 25, '5yr')\n",
+ "ig25 = get_index_quotes('IG', 25, '5yr')\n",
+ "ig25_tranches = ig25_tranches[['attach', 'trancheupfrontmid','indexrefspread','tranchedelta']]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25_tranches = ig25_tranches.groupby([pd.TimeGrouper('D', level=0), 'attach']).last().dropna()\n",
+ "ig25_tranches = ig25_tranches.reset_index('attach', drop=False)\n",
+ "ig25 = ig25[['closeprice','duration','closespread']]\n",
+ "ig25_data = ig25_tranches.join(ig25)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25_data = ig25_data.assign(tranche_adjusted =\n",
+ " lambda x: x.trancheupfrontmid - \\\n",
+ " x.tranchedelta * (x.indexrefspread-x.closespread) * x.duration/100)\n",
+ "ig25_data = ig25_data.set_index('attach', append=True)\n",
+ "ig25_data.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#compute the accrued\n",
+ "ig25_data['accrued'] = ig25_data.groupby(level='attach')['tranche_adjusted'].transform(lambda x: x.index.levels[0].to_series().diff().astype('timedelta64[D]')*1/360)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#compute the daily pnl\n",
+ "ig25_data['index_pnl'] = (ig25_data.groupby(level='attach')['closeprice'].\n",
+ " apply(lambda x:x.diff()))\n",
+ "ig25_data['tranche_pnl'] = (ig25_data.groupby(level='attach')['tranche_adjusted'].\n",
+ " apply(lambda x:-x.diff()))\n",
+ "for col in ['index_pnl', 'tranche_pnl']:\n",
+ " ig25_data[col] += ig25_data.accrued"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25_data.xs(3, level='attach').plot(x='index_pnl', y='tranche_pnl', kind='scatter')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import statsmodels.api as sm\n",
+ "import statsmodels.formula.api as smf\n",
+ "import numpy as np\n",
+ "model = smf.ols('tranche_pnl~0+index_pnl', data=ig25_data.xs(15, level='attach'))\n",
+ "results = model.fit()\n",
+ "results.summary()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def rolling_ols(df, formula, window=20):\n",
+ " r = []\n",
+ " for i in range(len(df)-window):\n",
+ " model = smf.ols(formula, data=df.iloc[i:(20+i),])\n",
+ " results = model.fit()\n",
+ " r.append(results.params)\n",
+ " r = pd.concat(r, axis=1).T\n",
+ " return r.set_index(df.index[20:])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "rols = rolling_ols(ig25_data, 'tranche_pnl~0+index_pnl+np.square(index_pnl)')\n",
+ "rols.columns = ['delta','gamma']\n",
+ "test = rols.join(ig25_data)[['delta','tranchedelta']]\n",
+ "test.plot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25_data.closespread.apply(np.log).diff().std()*np.sqrt(250)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25 = get_index_quotes('IG', 25, '5yr')\n",
+ "ig25['closespread'].rolling(20).apply(lambda x: np.std(np.diff(np.log(x)))*np.sqrt(250)).plot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25 = get_index_quotes('IG', 25, '5yr')\n",
+ "returns = ig25.closespread.pct_change().dropna()\n",
+ "returns.plot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from arch import arch_model\n",
+ "am = arch_model(returns, mean='ARX', lags=1, vol='GARCH', o=1)\n",
+ "res = am.fit(update_freq=5)\n",
+ "res.summary()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "res.plot(annualize='D')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import math\n",
+ "annualized_vol = res.conditional_volatility * math.sqrt(252)\n",
+ "annualized_vol.plot()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "returns[returns.abs()>0.04].groupby(pd.TimeGrouper('M')).count()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "returns['2016-03']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ig25.index"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pd.read_sql?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.1"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}