Notebook

13D Filings Event Study

In [19]:
# Some imports that we'll use later on
import pandas as pd
import numpy as np
import seaborn as sns
from datetime import timedelta
import matplotlib.pyplot as plt

# For use in Quantopian Research
# To try out the free sample
# from quantopian.interactive.data.eventvestor import earnings_calendar_free as earnings_calendar
# Free version
# study_date_ranges = range(2011, 2014)

# For using the full premium version (available for $5/ mo) which this study is based off of
from quantopian.interactive.data.eventvestor import _13d_filings as dataset
# Premium Version
study_date_ranges = range(2011, 2016)

# Next import Blaze and odo (which allows us to work between Blaze and Pandas)
from odo import odo
import blaze as bz
In [10]:
dataset.dshape
Out[10]:
dshape("""var * {
  event_id: float64,
  asof_date: datetime,
  trade_date: ?datetime,
  symbol: string,
  event_type: ?string,
  event_headline: ?string,
  percent_shares: float64,
  number_shares: float64,
  acquiring_entity: ?string,
  event_rating: float64,
  timestamp: datetime,
  sid: int64
  }""")
In [20]:
#: Enter in the name of the DATE column that you're using as the event date
date_column = 'asof_date'

#: Enter in the name of the SYMBOL column that contains the symbols you're looking at
symbol_column = 'symbol'

#: Type of pricing field to compare
pricing_type = 'close_price'
In [26]:
earnings_ranges = {}
for date in study_date_ranges:
    earnings_ranges[date] = [pd.to_datetime('%s-01-01' % date), pd.to_datetime("%s-12-31" % date)]

# We are separating out a pandas DataFrame for each year
dataset_calendars = {}
for date, date_ranges in earnings_ranges.iteritems():
    temp_dataset = dataset[dataset[date_column] > min(date_ranges)]
    temp_dataset = temp_dataset[temp_dataset[date_column] < max(date_ranges)]
    dataset_calendars[date] = odo(temp_dataset, pd.DataFrame)
    
starting_point = 10

Calculating Price Returns During Filing Periods

In [42]:
def get_windows_of_returns(data, event_type, window_side_length=2):
    change_windows = {}
    total_rows = len(data.index)
    x = 0
    for event in data.iterrows():
        event_date = event[1][event_type]
        event_id = event[1]['event_id']
        # The hard coded 25 is merely to provide a buffer, to ensure we get enough days.
        window_start = event_date - pd.Timedelta('%s Days' % str(window_side_length + 20))
        window_end = event_date + pd.Timedelta('%s Days' % str(window_side_length + 20))

        symbol = event[1]["symbol"]
        try:
            prev_prices = get_pricing(symbol, start_date=window_start, end_date=event_date, fields=pricing_type).ffill()[-window_side_length-1:]
            after_prices = get_pricing(symbol, start_date=event_date, end_date=window_end, fields=pricing_type).ffill()[:window_side_length+1]
        except:
            continue
        prices = prev_prices.append(after_prices).drop_duplicates()
        asset_returns = prices.pct_change().ffill().fillna(0)
        # In case there's no data (e.g. delisted security, skip)
        if np.average(asset_returns) == 0:#len(asset_returns.index) < (window_side_length*2+1):
            continue
        change_windows[event_id] = asset_returns.tolist()  # Using .tolist() strips the data from time indexing
        x+=1
    print "Observed %s events" % x
    return pd.DataFrame(dict([ (k,pd.Series(v)) for k,v in change_windows.iteritems() ]))

returns_range = {}
for date, df in dataset_calendars.iteritems():
    print "Processing data for %s" % date
    returns = get_windows_of_returns(df, date_column, window_side_length=starting_point)
    # Shifting our index to show appropriate date numbers.
    # Start t-1 at 0
    returns.index = returns.index - starting_point
    returns_range[date] = returns
Processing data for 2011
Observed 230 events
Processing data for 2012
Observed 220 events
Processing data for 2013
Observed 206 events
Processing data for 2014
Observed 292 events
Processing data for 2015
Observed 343 events
In [43]:
pd.DataFrame(dict([ (k,pd.Series(v)) for k,v in returns.iteritems() ]))
Out[43]:
1819717.0 1820172.0 1821831.0 1821853.0 1821855.0 1823367.0 1823786.0 1824117.0 1824118.0 1824124.0 ... 2104546.0 2105472.0 2105761.0 2105762.0 2105764.0 2105765.0 2105766.0 2105904.0 2105905.0 2105996.0
-10 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
-9 0.025116 0.039548 0.007588 -0.010122 0.006094 -0.003992 0.021825 0.007407 -0.018153 -0.030287 ... -0.131561 -0.009259 -0.051326 -0.028638 -0.019776 -0.023810 -0.084567 -0.027106 -0.035040 -0.032258
-8 0.041742 -0.027174 -0.000655 0.013806 -0.000387 0.002004 -0.033010 0.125000 -0.003625 -0.018405 ... -0.045125 -0.084206 0.051398 -0.050199 -0.039258 -0.043646 -0.099307 0.042169 0.005587 0.001333
-7 -0.003484 0.004190 -0.012779 -0.004541 -0.008896 -0.012000 -0.030120 0.026144 0.027106 0.002841 ... 0.032068 -0.020512 0.034305 0.016779 0.015323 0.028188 -0.012821 0.028902 -0.006243 -0.062051
-6 0.004371 -0.001391 -0.010621 -0.014192 -0.012228 0.004049 0.012422 0.012739 0.009387 0.002833 ... -0.005724 -0.021880 0.078773 0.011551 0.062046 0.018277 0.011688 0.007725 -0.006296 -0.008092
-5 0.000870 0.041783 0.001006 0.000514 0.001844 0.014113 0.008180 -0.044025 -0.008773 0.061582 ... -0.043586 -0.041329 0.001537 -0.006525 0.006316 0.006410 -0.060334 0.001394 -0.007736 0.209961
-4 0.008261 0.029412 -0.055630 -0.015930 -0.026949 0.015905 0.014199 -0.013158 -0.024606 -0.013305 ... 0.034394 0.066778 0.009977 -0.008210 0.020921 -0.014013 -0.016393 0.011830 0.012048 -0.101845
-3 0.015524 0.002597 -0.041874 -0.003133 -0.018103 0.003914 0.002000 -0.006667 0.002541 -0.014024 ... 0.027431 -0.114051 -0.010638 0.016556 0.004611 0.012920 -0.002778 0.004814 0.105742 0.093112
-2 -0.007219 0.036269 0.005556 -0.020430 0.032471 0.023392 0.023952 -0.053691 -0.007784 -0.062090 ... -0.008091 0.011169 0.076805 -0.011401 -0.020398 0.035714 0.284123 0.020534 0.073464 0.033614
-1 -0.006843 0.017500 0.008840 0.013369 0.005864 -0.003810 -0.009747 0.170213 -0.023536 0.046369 ... -0.029364 0.104523 0.079886 0.036244 0.022905 0.049261 0.127983 -0.011402 0.008260 -0.009325
0 -0.006891 -0.127764 0.079226 -0.007388 -0.013514 0.011472 0.023622 -0.133333 0.053251 0.017726 ... -0.016807 0.284211 -0.036328 -0.020668 0.063104 0.009390 0.213462 0.072592 -0.009362 0.003177
1 -0.025152 -0.045070 -0.017253 0.000532 -0.010610 -0.011323 0.011538 0.020979 0.016143 0.049403 ... 0.020513 -0.073770 -0.062371 0.021916 -0.053135 -0.024419 -0.015848 0.024668 0.003544 0.062515
2 -0.027580 -0.035398 0.037522 0.013284 -0.009638 0.001893 0.009506 -0.018630 0.018156 -0.000522 ... 0.030151 0.030973 0.009503 0.000794 0.040445 0.001192 -0.014493 -0.006173 0.010300 -0.005409
3 0.003202 -0.042813 -0.009622 -0.003671 -0.010005 -0.007634 0.032015 -0.008934 -0.001886 -0.007833 ... 0.008130 0.051502 -0.041274 0.014286 0.005345 0.021429 0.057190 -0.004969 0.057967 0.024750
4 0.058368 -0.023962 -0.021106 0.002500 -0.029212 NaN 0.003650 NaN 0.003264 -0.020000 ... -0.000806 -0.028571 0.032477 0.003130 -0.020300 -0.003497 -0.001546 -0.000624 -0.024229 -0.055020
5 -0.007324 -0.018003 -0.041068 -0.003549 0.009412 NaN 0.010909 NaN 0.008904 0.000537 ... -0.004036 0.008403 -0.055596 -0.001560 -0.039961 0.011696 -0.006192 -0.011243 -0.002822 -0.024756
6 0.004774 -0.001667 -0.005710 0.006328 -0.001837 NaN -0.003597 NaN 0.006110 0.023618 ... -0.001621 -0.075000 -0.015469 0.010937 -0.048304 -0.010405 -0.052960 -0.013266 0.003962 -0.012810
7 -0.004320 -0.068447 0.018665 0.039581 0.017834 NaN -0.014440 NaN -0.038968 -0.022549 ... 0.033279 0.144144 -0.030707 -0.044822 -0.116091 -0.015187 -0.014803 -0.035851 -0.009583 -0.045833
8 -0.019957 0.078853 -0.011628 0.008259 0.029898 NaN -0.010989 NaN 0.002457 0.015021 ... 0.003142 NaN -0.071429 -0.053398 -0.026268 -0.009490 0.003339 0.005312 -0.035857 -0.026825
9 0.010181 -0.013289 0.007843 0.075924 0.009452 NaN NaN NaN 0.014884 -0.002114 ... -0.002349 NaN 0.005245 NaN -0.135508 -0.029940 NaN -0.009908 -0.065525 -0.012949
10 NaN NaN NaN NaN 0.032638 NaN NaN NaN 0.030193 NaN ... NaN NaN -0.066087 NaN NaN NaN NaN NaN 0.005054 0.052344

21 rows × 343 columns

Plotting Cumulative Price Returns During Filing Periods

In [55]:
for year, returns in returns_range.iteritems():
    data = (returns + 1).cumprod() - 1
    avg_returns = pd.Series(index=returns.index[:19],
                            data=data.mean(axis=1),
                            name="%s, N=%s" % (year, len(data.columns)))
    avg_returns.plot()
    ax = plt.axes()  
    ax.spines['top'].set_color('none')
    ax.spines['bottom'].set_color('none')
    ax.yaxis.grid(color="none")
    plt.legend(loc='best')
plt.title("Average Daily Percent Change in Price During 13-D Filings (Close-to-Close)")
plt.xlabel("Number of days after 13-D Filing")
plt.ylabel("Percent Change")
Out[55]:
<matplotlib.text.Text at 0x7f1f0aa24d90>
In [ ]: