A minimal example of using EventStudies.jl
This is a basic example of how you can conduct an event study using EventStudies.jl. We create and use synthetic data, show how to use the EventStudies.jl API, and plot it!
First, we load the packages:
using TSFrames # EventStudies operates exclusively on TSFrames
using EventStudies
using CairoMakie # For plottingCreating a dataset
First, we define the TSFrame with the data we want to use. This is a TSFrame with 11 rows, and 2 columns, var1 and var2. They both follow the same general path, linearly increasing to 1, then staying at 1.
ts1 = TSFrame(
DataFrame(
:Index => 1:11,
:var1 => vcat(LinRange(0, 1, 5), ones(6)),
:var2 => vcat(LinRange(0, 1, 6), ones(5)),
)
)11×2 TSFrame with Int64 Index
Index var1 var2
Int64 Float64 Float64
─────────────────────────
1 0.0 0.0
2 0.25 0.2
3 0.5 0.4
4 0.75 0.6
5 1.0 0.8
6 1.0 1.0
7 1.0 1.0
8 1.0 1.0
9 1.0 1.0
10 1.0 1.0
11 1.0 1.0For convenience, I've directly referred to everything from EventStudies.jl as EventStudies.*. However, these are all exported, so you don't have to do that.
Computing the event study
Event studies are conceptually very simple - you take a time series and a list of events, and extract a window around each event. Then, you perform inference on these measurements to see if the event had a statistically significant effect on the time series, and what that effect was.
In order to conduct an event study, we need a list of events. This has to be passed as a Vector of Pair{Symbol, DateType}. The first element of the pair is the name of the column in ts1 that we want to use as the event. The second element is the time of the event. This can be in any type DateType which TSFrames supports, which for now is restricted to Int, Dates.Date, and Dates.DateTime.
event_list = [:var1 => 5, :var2 => 6, :var2 => 15]3-element Vector{Pair{Symbol, Int64}}:
:var1 => 5
:var2 => 6
:var2 => 15Our dataset is the form of absolute measurements, levels in EventStudies.jl parlance. However, event studies function best when given data in the form of returns, which are the difference between the log-transformed measurements, or diff(log(ts)). So, we'll convert our data to returns using the EventStudies.levels_to_returns function.
To do this, we'll use the EventStudies.eventstudy function, which takes in a TSFrame of returns, a list of events, and a window size.
eventtime_returns_ts, event_return_codes = EventStudies.eventstudy(
levels_to_returns(ts1), # the dataset, converted from "levels"
event_list, # the list of events, as :colname => event_time
2 # the width of the window, i.e., `-2:2`.
)(5×2 TSFrame with Int64 Index
Index var1 var2
Int64 Float64 Float64
─────────────────────────
-2 69.3147 40.5465
-1 40.5465 28.7682
0 28.7682 22.3144
1 0.0 0.0
2 0.0 0.0, EventStudies.EventStatus[EventStudies.Success(), EventStudies.Success(), EventStudies.WrongSpan()])The eventtime_returns_ts is a TSFrame with the same columns as ts1, but with a new index. This index takes the form of "event time", which is the time relative to the event.
All columns share the same index. statuses represents the status of each event as a EventStatus object. In this case, the first two events were successful (EventStudies.Success) but the last one was unsuccessful.
This is because the last event was too close to the end of the dataset to have a full window, so it was discarded, as indicated by the EventStudies.WrongSpan error code.
Note that in this case, we have 3 events, but only 2 columns. This is because the third event is too close to the end of the dataset to have a full window, so it's discarded.
Now, we can compute the cumulative returns, and the confidence intervals using the EventStudies.remap_cumsum and EventStudies.inference functions. The remap_cumsum function takes in a TSFrame of returns, and returns a TSFrame of cumulative returns. You can think of cumulative returns as a mapping from returns back to levels, except renormalized so that everything starts at 0.
eventtime_cum_ts = EventStudies.remap_cumsum(eventtime_returns_ts)5×2 TSFrame with Int64 Index
Index var1 var2
Int64 Float64 Float64
─────────────────────────
-2 0.0 0.0
-1 40.5465 28.7682
0 69.3147 51.0826
1 69.3147 51.0826
2 69.3147 51.0826Finally, we conduct inference on the cumulative returns. We'll use the EventStudies.BootstrapInference method, which takes in an inference method (see EventStudies.InferenceMethod) and the return timeseries. In this case, we use the bootstrap method of inference, which calls to Bootstrap.jl.
confints = EventStudies.inference(EventStudies.BootstrapInference(), eventtime_cum_ts)([0.0, 34.657359027997266, 60.198640216296795, 60.198640216296795, 60.198640216296795], [0.0, 28.7682072451781, 51.08256237659907, 51.08256237659907, 51.08256237659907], [0.0, 40.546510810816436, 69.31471805599452, 69.31471805599452, 69.31471805599452])Plotting
Now, we can plot the results. We'll use Makie.jl for this.
f, a, p = series(eventtime_cum_ts.Index, Matrix(eventtime_cum_ts)'; labels = ["Event 1", "Event 2"], axis = (xlabel = "Event time", ylabel = "Cumulative return (%)"))
band!(eventtime_cum_ts.Index, confints[2], confints[3]; color = Makie.wong_colors(0.5)[3], label = "95% Confidence interval")
lines!(eventtime_cum_ts.Index, confints[1]; label = "Mean")
axislegend(a; position = :rb)
fAnd that's our event study!
This page was generated using Literate.jl.