EventStudy.jl (3232B)
1 @testset "Event Study" begin 2 3 import Dates: Date, Day 4 import Statistics: mean 5 import Random: seed! 6 7 seed!(42) # deterministic random draws 8 9 # Build synthetic daily returns panel: 2 firms, 300 trading days 10 dates = Date("2010-01-04"):Day(1):Date("2011-04-01") 11 # Remove weekends for realistic trading calendar 12 trading_days = filter(d -> Dates.dayofweek(d) <= 5, dates) 13 trading_days = trading_days[1:300] # exactly 300 days 14 15 n = length(trading_days) 16 df_ret = DataFrame( 17 permno = vcat(fill(1, n), fill(2, n)), 18 date = vcat(trading_days, trading_days), 19 ret = vcat(0.001 .+ 0.01 .* randn(n), 0.0005 .+ 0.015 .* randn(n)), 20 mktrf = vcat(repeat([0.0005 + 0.008 * randn()], n), repeat([0.0005 + 0.008 * randn()], n)) 21 ) 22 # Regenerate mktrf properly (same market for both firms on same date) 23 mkt_returns = 0.0005 .+ 0.008 .* randn(n) 24 df_ret.mktrf = vcat(mkt_returns, mkt_returns) 25 26 # Inject a large positive event: +20% abnormal return on event day for firm 1 27 # Must be large enough to dominate cumulative noise over the 21-day event window 28 event_idx_firm1 = 270 # well within bounds for estimation window 29 df_ret.ret[event_idx_firm1] += 0.20 30 31 events = DataFrame( 32 permno = [1, 2], 33 event_date = [trading_days[event_idx_firm1], trading_days[280]] 34 ) 35 36 # ---- Market-adjusted model ---- 37 @testset "Market-adjusted" begin 38 result = event_study(events, df_ret; model=:market_adjusted) 39 @test nrow(result) == 2 40 @test "car" in names(result) 41 @test "bhar" in names(result) 42 @test "n_obs" in names(result) 43 @test !ismissing(result.car[1]) 44 @test !ismissing(result.car[2]) 45 @test result.n_obs[1] == 21 # -10 to +10 inclusive 46 # Firm 1 should have positive CAR (we injected +20%) 47 @test result.car[1] > 0.10 48 end 49 50 # ---- Market model ---- 51 @testset "Market model" begin 52 result = event_study(events, df_ret; 53 model=:market_model, 54 event_window=(-5, 5), 55 estimation_window=(-250, -11)) 56 @test nrow(result) == 2 57 @test !ismissing(result.car[1]) 58 @test result.n_obs[1] == 11 # -5 to +5 59 end 60 61 # ---- Mean-adjusted model ---- 62 @testset "Mean-adjusted" begin 63 result = event_study(events, df_ret; 64 model=:mean_adjusted, 65 event_window=(-3, 3), 66 estimation_window=(-200, -11)) 67 @test nrow(result) == 2 68 @test !ismissing(result.car[1]) 69 @test result.n_obs[1] == 7 # -3 to +3 70 end 71 72 # ---- Edge cases ---- 73 @testset "Edge cases" begin 74 # Entity not in returns 75 events_missing = DataFrame(permno=[9999], event_date=[Date("2010-06-01")]) 76 result = event_study(events_missing, df_ret) 77 @test ismissing(result.car[1]) 78 @test result.n_obs[1] == 0 79 80 # Event too early (no estimation window) 81 events_early = DataFrame(permno=[1], event_date=[trading_days[5]]) 82 result = event_study(events_early, df_ret) 83 @test ismissing(result.car[1]) 84 85 # Invalid model 86 @test_throws ArgumentError event_study(events, df_ret; model=:foo) 87 end 88 89 end