# The pre-holiday effect on SPY, daily closes - specification (RIP Backtest protocol v1.0 plus the 8 Oct 2026 rules)

Specification written and **frozen 2026-11-04 (approved by the owner the same day: "Da, rulează"; draft 1 unchanged)**. No pre-holiday trade, return or equity curve of this test has been computed. Looked at before writing it: the holiday calendar against the SPY file's trading days (`node ph_calendar.js`, which computes no return): the file's 309 missing weekdays are exactly the 298 scheduled holidays given by the rules below plus 11 unscheduled closures. Once frozen, any change of a rule is documented and treated as a separate test; bugs may be fixed and are logged at the end.

## Why this test, and where the rule comes from
- **Chosen by the owner** on 2026-11-04 ("Ziua dinaintea sărbătorilor") from three proposals (the pre-holiday effect on SPY, night versus day on SPY, the pre-FOMC drift).
- **The effect.** The last trading day before a market holiday has been one of the strongest days of the year for US stocks. Lakonishok and Smidt (Review of Financial Studies, 1988) found it in 90 years of the Dow; Ariel ("High Stock Returns before Holidays: Existence and Evidence on Possible Causes", Journal of Finance, 1990) found pre-holiday returns several times the average day's and could not explain them by other calendar effects; Kim and Park (Journal of Financial and Quantitative Analysis, 1994) found it in the UK and Japan too.
- **Why it could work:** a mood or positioning effect before a break (short sellers closing before a long weekend, buyers keen to be in), and thin pre-holiday sessions that move on little volume.
- **Why it might not (written before the run):** it has been in print since 1988 and later studies reported it weaker (for example Vergin and McGinnis, Applied Financial Economics, 1999, "Revisiting the holiday effect: is it on holiday?"). One day per trade leaves little room after a cost. Some pre-holiday days fall at the turn of the month (for example 31 December), which we tested separately; the overlap is reported.
- **Looked at before on the site:** the same SPY file in the SMA 200, Accumulation/Distribution and turn-of-the-month tests, so the holdout below is already used; the turn-of-the-month test is the closest relative (a calendar rule on the same file).

## Instrument and data
- **SPY**, daily bars 1993-01-29 to 2026-10-20 (8,489 trading days) with 136 dividends, the files of the SMA 200 test (hashes: spy_daily.csv 8de2b35662134fe5, spy_dividends.csv f6a7857257bbd23c, dtb3_fred_raw.csv 0ba63bf126e01aa6). Total return: each dividend reinvested at its ex-date close.
- **Scheduled holidays** (full-day NYSE closures, `ph_calendar.js`): New Year's Day (1 January; a Sunday moves it to Monday, a Saturday means no holiday), Martin Luther King Jr. Day (third Monday of January, from 1998), Washington's Birthday (third Monday of February), Good Friday, Memorial Day (last Monday of May), Juneteenth (19 June, from 2022), Independence Day (4 July), Labor Day (first Monday of September), Thanksgiving (fourth Thursday of November), Christmas (25 December); a fixed-date holiday on a Saturday is observed on the Friday, on a Sunday on the Monday. Checked against the file: no scheduled holiday is a trading day, and every other missing weekday is one of the listed unscheduled closures.
- **Unscheduled closures are not holidays** (they were not known in advance): 27 April 1994, 11 to 14 September 2001, 11 June 2004, 2 January 2007, 29 and 30 October 2012, 5 December 2018, 9 January 2025.

## Rule (one variant, fixed now)
- **PH:** for each scheduled holiday, the **pre-holiday day** is the last trading day before it. Buy at the **close of the trading day before the pre-holiday day** and sell at the **close of the pre-holiday day**: the trade earns the pre-holiday day's return.
- Fills at the close (market-on-close orders); the days are known in advance from the calendar. Long only, all-in, no stop. A holiday whose pre-holiday day or entry day is outside the file is not traded.
- Growth of a trade bought at the close of day j and sold at the close of day k = j+1: (close[k] + dividend[k]) / close[j].

## Costs
- **0.04% of the price per round trip** (0.02% a side), as in the other SPY tests; 0.10% shown as a scenario. The verdict is that of 0.04%.

## Results, periods, gates, control
- **Unit:** net % per trade.
- **Periods (by entry date):** development 1993 .. 2005; validation 2006 .. 2015; holdout 2016 .. 2026-10-20 (**already used** by the SMA 200, Accumulation/Distribution and turn-of-the-month tests).
- **Six gates:** validation profit factor >= 1.2; at least 50 trades in each period; development **t >= 2.0** (one variant of a strategy not tested before on the site); above the 95th percentile of 500 runs of the matched control; positive without the 10 best trades; holdout net positive (flagged used). Fewer than 30 trades: Inconclusive.
- **Matched control (random days in the same months):** for each real trade, a one-day hold whose entry close is a trading day drawn at random, uniformly, from the same calendar month as the real entry (any day of that month whose exit falls inside the file); same costs. 500 runs, seeds 1000 + run. Statistic: average net % per trade. It keeps the number of trades, their length and their months, and removes only the holiday timing.

## Also reported (descriptive, after the gates; nothing here can change the rule or the verdict)
- By holiday; the post-holiday day (the first trading day after the holiday); pre-holiday days inside the turn-of-the-month window (days -1 to +3) against the others; all other days of the file.
- Neighbouring holds: the two days before the holiday (close of the third day before it to the close of the pre-holiday day), across the holiday (close of the pre-holiday day to the close of the first day after), and both together.
- The whole strategy with T-bill interest while out, against buy-and-hold; year by year; by period; without the 10 and the 20 best trades; win rate with its Wilson interval.

## Engine and self-test (before the run)
New engine `ph_lib.js`, an independent re-implementation `ph_ref.js` (its own holiday list, built by checking every calendar day), and `ph_selftest.js`, which must pass before any result is printed: the holiday rules on known years (Easter dates, observed days, the Saturday New Year, MLK from 1998, Juneteenth from 2022), hand-built weeks (a Monday holiday, a Thursday holiday, Good Friday, a dividend on the pre-holiday day, an unscheduled closure that is not traded, the ends of the file), the engine equal to the reference trade by trade on the real data, and a control run whose drawn entries are the real ones reproducing the real trades. `ph_run.js` refuses to run unless the self-test passed on the current code and this file is frozen.

## Before the article
Results go to the owner first (in Romanian), with every gate. One article, only with the owner's agreement.

## Reading the result
"Buried" or "Inconclusive" mean a statistically convincing edge per trade was not demonstrated; neither is proof that none exists. A "Survivor" passed our six gates on one ETF with a holdout already used; it is not a promise. One ETF, one data source, the published rule only.

## Log
- 2026-11-04: draft 1 written. Nothing computed.
- 2026-11-04: frozen (draft 1 unchanged), approved by the owner. SHA-256 of the frozen text (before this log grew): 958b75e73a12a79461a383351bbb5ad43e5c7c81cb6fbf1171c75e716abf18e0.
- 2026-11-04, before any result: ph_selftest.js passed (holiday rules on known years, the reference's day-by-day holiday test equal to the rules on every day of 1993-2026, hand-built weeks, engine = reference on the real data, control with the real entries drawn). Counts only: 298 trades (development 109, validation 89, holdout 100); 106 pre-holiday days in the turn-of-the-month window.
- 2026-11-04, the run (`node ph_run.js`, once, outputs in `ph_out/`): **PH Buried, 3 of 6** (fails development t -0.08, the control at the 82.6th percentile and without the 10 best -8.38%; passes validation PF 1.20, the trade counts and the holdout +13.97% PF 1.77). Rule 3 not applicable. The neighbouring holds were looked at only after the gates, as written above; none passed more than 3 of 6. Nothing was rerun or changed.
- 2026-11-04: the owner asked for the article ("Da, scrie-l"). Published from the outputs of the single run, unchanged.
