{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Alphalens: event study"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "While Alphalens is a tool designed to evaluate a cross-sectional signal which can be used to rank many securities each day, we can still make use of Alphalens returns analysis functions, a subset of Alphalens, to create a meaningful event study.\n",
    "\n",
    "An event study is a statistical method to assess the impact of a particular event on the value of a stock. In this example we will evalute what happens to stocks whose price fall below 30$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Populating the interactive namespace from numpy and matplotlib\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/lucasca/.local/lib/python3.5/site-packages/statsmodels/compat/pandas.py:56: FutureWarning: The pandas.core.datetools module is deprecated and will be removed in a future version. Please use the pandas.tseries module instead.\n",
      "  from pandas.core import datetools\n"
     ]
    }
   ],
   "source": [
    "%pylab inline --no-import-all\n",
    "import alphalens\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import datetime"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Below is a simple mapping of tickers to sectors for a universe of 500 large cap stocks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "tickers = [ 'ACN', 'ATVI', 'ADBE', 'AMD', 'AKAM', 'ADS', 'GOOGL', 'GOOG', 'APH', 'ADI', 'ANSS', 'AAPL',\n",
    "'AVGO', 'CA', 'CDNS', 'CSCO', 'CTXS', 'CTSH', 'GLW', 'CSRA', 'DXC', 'EBAY', 'EA', 'FFIV', 'FB',\n",
    "'FLIR', 'IT', 'GPN', 'HRS', 'HPE', 'HPQ', 'INTC', 'IBM', 'INTU', 'JNPR', 'KLAC', 'LRCX', 'MA', 'MCHP',\n",
    "'MSFT', 'MSI', 'NTAP', 'NFLX', 'NVDA', 'ORCL', 'PAYX', 'PYPL', 'QRVO', 'QCOM', 'RHT', 'CRM', 'STX',\n",
    "'AMG', 'AFL', 'ALL', 'AXP', 'AIG', 'AMP', 'AON', 'AJG', 'AIZ', 'BAC', 'BK', 'BBT', 'BRK.B', 'BLK', 'HRB',\n",
    "'BHF', 'COF', 'CBOE', 'SCHW', 'CB', 'CINF', 'C', 'CFG', 'CME', 'CMA', 'DFS', 'ETFC', 'RE', 'FITB', 'BEN',\n",
    "'GS', 'HIG', 'HBAN', 'ICE', 'IVZ', 'JPM', 'KEY', 'LUK', 'LNC', 'L', 'MTB', 'MMC', 'MET', 'MCO', 'MS',\n",
    "'NDAQ', 'NAVI', 'NTRS', 'PBCT', 'PNC', 'PFG', 'PGR', 'PRU', 'RJF', 'RF', 'SPGI', 'STT', 'STI', 'SYF', 'TROW',\n",
    "'ABT', 'ABBV', 'AET', 'A', 'ALXN', 'ALGN', 'AGN', 'ABC', 'AMGN', 'ANTM', 'BCR', 'BAX', 'BDX', 'BIIB', 'BSX',\n",
    "'BMY', 'CAH', 'CELG', 'CNC', 'CERN', 'CI', 'COO', 'DHR', 'DVA', 'XRAY', 'EW', 'EVHC', 'ESRX', 'GILD', 'HCA',\n",
    "'HSIC', 'HOLX', 'HUM', 'IDXX', 'ILMN', 'INCY', 'ISRG', 'IQV', 'JNJ', 'LH', 'LLY', 'MCK', 'MDT', 'MRK', 'MTD',\n",
    "'MYL', 'PDCO', 'PKI', 'PRGO', 'PFE', 'DGX', 'REGN', 'RMD', 'SYK', 'TMO', 'UNH', 'UHS', 'VAR', 'VRTX', 'WAT',\n",
    "'MMM', 'AYI', 'ALK', 'ALLE', 'AAL', 'AME', 'AOS', 'ARNC', 'BA', 'CHRW', 'CAT', 'CTAS', 'CSX', 'CMI', 'DE',\n",
    "'DAL', 'DOV', 'ETN', 'EMR', 'EFX', 'EXPD', 'FAST', 'FDX', 'FLS', 'FLR', 'FTV', 'FBHS', 'GD', 'GE', 'GWW',\n",
    "'HON', 'INFO', 'ITW', 'IR', 'JEC', 'JBHT', 'JCI', 'KSU', 'LLL', 'LMT', 'MAS', 'NLSN', 'NSC', 'NOC', 'PCAR',\n",
    "'PH', 'PNR', 'PWR', 'RTN', 'RSG', 'RHI', 'ROK', 'COL', 'ROP', 'LUV', 'SRCL', 'TXT', 'TDG', 'UNP', 'UAL',\n",
    "'AES', 'LNT', 'AEE', 'AEP', 'AWK', 'CNP', 'CMS', 'ED', 'D', 'DTE', 'DUK', 'EIX', 'ETR', 'ES', 'EXC']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Load the prices."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "import pandas_datareader.data as web\n",
    "pan = web.DataReader(tickers, \"google\", datetime.datetime(2015, 6, 1),  datetime.datetime(2017, 1, 1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "pan = pan.transpose(2,1,0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Now it's time to build the events DataFrame, the input will give to Alphalens.\n",
    "\n",
    "Alphalens calculates statistics for those dates where the input DataFrame has values (not NaN). So to compute the performace analysis on specific dates and securities (like an event study) then we have to make sure the input DataFrame contains valid values only on those date/security combinations where the event happens. All the other values in the DataFrame must be NaN or not present.\n",
    "\n",
    "Also, make sure the event values are positive (it doesn't matter the value but they must be positive) if you intend to go long on the events and use negative values if you intent to go short. This impacts the cumulative returns plots. \n",
    "\n",
    "Let's create the event DataFrame where we \"mark\" (any value) each day a security price fall below 30$. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "date        asset\n",
       "2015-06-02  ETFC     29.32\n",
       "2015-06-03  CA       29.96\n",
       "2015-06-04  LNT      29.64\n",
       "            PWR      29.85\n",
       "2015-06-15  CA       29.93\n",
       "2015-06-18  PWR      29.87\n",
       "2015-06-25  PWR      29.63\n",
       "2015-06-29  CA       29.48\n",
       "2015-06-30  ETFC     29.74\n",
       "            HRB      29.73\n",
       "2015-07-07  INTC     29.87\n",
       "2015-07-10  LNT      29.80\n",
       "2015-07-17  INTC     29.66\n",
       "2015-07-20  EBAY     26.89\n",
       "2015-07-22  FLIR     29.62\n",
       "            LNT      29.81\n",
       "2015-07-24  ARNC     29.73\n",
       "            CA       29.81\n",
       "            ETFC     28.97\n",
       "2015-07-29  LNT      29.92\n",
       "2015-08-03  ARNC     29.46\n",
       "2015-08-11  CSX      29.98\n",
       "2015-08-20  FLIR     29.90\n",
       "2015-08-24  AOS      28.94\n",
       "            LNT      29.62\n",
       "            NTAP     28.96\n",
       "            PGR      28.73\n",
       "            SCHW     28.44\n",
       "2015-08-26  NTAP     29.64\n",
       "            SCHW     29.87\n",
       "                     ...  \n",
       "2016-06-08  IVZ      29.84\n",
       "2016-06-14  SYF      29.26\n",
       "2016-06-16  AAL      29.80\n",
       "2016-06-23  GE       29.15\n",
       "2016-06-24  AAL      28.20\n",
       "            ARNC     28.68\n",
       "2016-06-27  FLIR     29.51\n",
       "            MAS      29.77\n",
       "2016-07-15  STX      29.40\n",
       "2016-08-11  SCHW     29.71\n",
       "2016-08-23  GE       29.95\n",
       "2016-09-07  GE       29.70\n",
       "2016-09-12  ARNC     28.56\n",
       "2016-10-11  ARNC     29.73\n",
       "2016-10-12  FLIR     29.97\n",
       "2016-10-17  IVZ      29.97\n",
       "2016-10-19  IVZ      29.74\n",
       "2016-10-20  EBAY     29.50\n",
       "2016-10-26  IVZ      29.40\n",
       "2016-11-02  MAS      29.94\n",
       "2016-11-04  CSCO     29.98\n",
       "            PFE      29.91\n",
       "2016-11-14  EXC      29.90\n",
       "2016-11-17  CSCO     29.91\n",
       "2016-11-23  CSCO     29.83\n",
       "2016-11-29  CSCO     29.94\n",
       "2016-12-06  JEC      29.96\n",
       "2016-12-09  CSCO     29.98\n",
       "2016-12-14  EBAY     29.85\n",
       "2016-12-15  JEC      29.94\n",
       "Length: 145, dtype: float64"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "today_price = pan.loc[:,:,'Open']\n",
    "yesterday_price = today_price.shift(1)\n",
    "events = today_price[ (today_price < 30.0) & (yesterday_price >= 30) ]\n",
    "events = events.stack()\n",
    "events.index = events.index.set_names(['date', 'asset'])\n",
    "events = events.astype(float)\n",
    "events"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "The pricing data passed to alphalens should contain the entry price for the assets so it must reflect the next available price after an event was observed at a given timestamp. Those prices must not be used in the calculation of the events for that time. Always double check to ensure you are not introducing lookahead bias to your study.\n",
    "\n",
    "The pricing data must also contain the exit price for the assets, for period 1 the price at the next timestamp will be used, for period 2 the price after 2 timestats will be used and so on.\n",
    "\n",
    "While Alphalens is time frequency agnostic, in our example we build 'pricing' DataFrame so that for each event timestamp it contains the assets open price for the next day afer the event is detected, this price will be used as the assets entry price. Also, we are not adding additional prices so the assets exit price will be the following days open prices (how many days depends on 'periods' argument)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "pricing = pan.loc[:,:,'Open'].iloc[1:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Run Event Style Returns Analysis"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "source": [
    "Before running Alphalens beware of some important options: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "# we don't want any filtering to be done\n",
    "\n",
    "filter_zscore = None "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "# We want to have only one  bin/quantile. So we can either use quantiles=1 or bins=1\n",
    "\n",
    "quantiles = None\n",
    "bins      = 1\n",
    "\n",
    "# Beware that in pandas versions below 0.20.0 there were few bugs in panda.qcut and pandas.cut\n",
    "# that resulted in ValueError exception to be thrown when identical values were present in the\n",
    "# dataframe and 1 quantile/bin was selected.\n",
    "# As a workaroung use the bins custom range option that include all your values. E.g.\n",
    "\n",
    "quantiles = None\n",
    "bins      = [-1000000,1000000]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "# You don't have to directly set 'long_short' option when running alphalens.tears.create_event_study_tear_sheet\n",
    "# But in case you are making use of other Alphalens functions make sure to set 'long_short=False'\n",
    "# if you set 'long_short=True' Alphalens will perform forward return demeaning and that makes sense only\n",
    "# in a dollar neutral portfolio. With an event style signal you cannot usually create a dollar neutral\n",
    "# long/short portfolio\n",
    "\n",
    "long_short = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dropped 29.0% entries from factor data: 29.0% in forward returns computation and 0.0% in binning phase (set max_loss=0 to see potentially suppressed Exceptions).\n",
      "max_loss is 35.0%, not exceeded: OK!\n"
     ]
    }
   ],
   "source": [
    "factor_data = alphalens.utils.get_clean_factor_and_forward_returns(events, \n",
    "                                                                   pricing, \n",
    "                                                                   quantiles=None,\n",
    "                                                                   bins=1,\n",
    "                                                                   periods=(1, 2, 3, 4, 5, 6, 10),\n",
    "                                                                   filter_zscore=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true,
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantiles Statistics\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>min</th>\n",
       "      <th>max</th>\n",
       "      <th>mean</th>\n",
       "      <th>std</th>\n",
       "      <th>count</th>\n",
       "      <th>count %</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>factor_quantile</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>26.89</td>\n",
       "      <td>29.99</td>\n",
       "      <td>29.645728</td>\n",
       "      <td>0.419674</td>\n",
       "      <td>103</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                   min    max       mean       std  count  count %\n",
       "factor_quantile                                                   \n",
       "1                26.89  29.99  29.645728  0.419674    103    100.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b5b5b748>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 79760 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b4a1b2b0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b49cf710>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 68576 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b3e89940>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 407780 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b409e400>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "alphalens.tears.create_event_study_tear_sheet(factor_data, pricing, avgretplot=(5, 10))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "### If we wanted to analyze the performance of  short signal, we only had to switch from positive to negative event values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "events = -events"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dropped 29.0% entries from factor data: 29.0% in forward returns computation and 0.0% in binning phase (set max_loss=0 to see potentially suppressed Exceptions).\n",
      "max_loss is 35.0%, not exceeded: OK!\n"
     ]
    }
   ],
   "source": [
    "factor_data = alphalens.utils.get_clean_factor_and_forward_returns(events, \n",
    "                                                                   pricing, \n",
    "                                                                   quantiles=None,\n",
    "                                                                   bins=1,\n",
    "                                                                   periods=(1, 2, 3, 4, 5, 6, 10),\n",
    "                                                                   filter_zscore=None)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantiles Statistics\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>min</th>\n",
       "      <th>max</th>\n",
       "      <th>mean</th>\n",
       "      <th>std</th>\n",
       "      <th>count</th>\n",
       "      <th>count %</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>factor_quantile</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>-29.99</td>\n",
       "      <td>-26.89</td>\n",
       "      <td>-29.645728</td>\n",
       "      <td>0.419674</td>\n",
       "      <td>103</td>\n",
       "      <td>100.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                   min    max       mean       std  count  count %\n",
       "factor_quantile                                                   \n",
       "1               -29.99 -26.89 -29.645728  0.419674    103    100.0"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b5386e48>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 79760 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b5376978>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b413aa90>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 68576 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b41085f8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 405784 characters]\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fe1b4ac8b00>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "alphalens.tears.create_event_study_tear_sheet(factor_data, pricing, avgretplot=(5, 10))"
   ]
  }
 ],
 "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
}
