Seasonal strategy

Turn of the Month

Holds SPY through the seasonally strong month-boundary window — the 5th-last trading day of the month to the 3rd trading day of the next.

Production
Seasonal
SPY
Backtest

Performance

Real internal-backtest statistics from the registry — Internal backtest · SPY daily bars (Yahoo Finance) · 401 monthly windows · 1993–2026.

62.6%
Win rate
1.85
Profit factor
+0.61%
Avg / trade
20.32%
Max drawdown
CAGR
not published
Exposure
not published
401
Total trades
7d
Avg holding

CAGR and exposure are not published for the library yet — we show “—” rather than invent them. Max drawdown, best year and worst year are undefined for the pooled Momentum universe backtest. Win rate is a share of trades, average return is per trade, and holding period is in trading days.

Historical pattern

Turn of the Month — average cumulative return across the held window

Trading days relative to month-end (ME = last trading day; entry T−4, exit T+3) · Average cumulative return (%). Empirical average path: the mean cumulative return at each held session, computed across all completed backtest windows on real SPY daily closes — measured from history, NOT illustrative and NOT externally published. Anchored at entry (T−4 = the 5th-last trading day); each point averages the windows that reached it (T−4…T+1 all 401, T+2 400, T+3 398 — three windows were holiday-shortened). It is labelled basis:"derived" only because the frozen v1 enum offers no "empirical" value, and "derived" (our own computation) is more honest here than "published" (which would imply an external source).

Decades of academic and practitioner research have found that a large fraction of the equity market's long-run gains has historically accrued in a narrow window around month-end — the last few trading days of one month plus the first few of the next — rather than being spread evenly across all sessions.

This strategy monetizes that window with one fully mechanical trade per month:

  • Buy SPY at the close of the 5th-to-last trading day of the month.
  • Sell at the close of the 3rd trading day of the following month.
  • Hold cash the rest of the time.

It is a market-timing strategy: entry and exit are fixed trading dates, not price levels. It recommends no individual stocks and defines no stop or target — its only risk control is the short, fixed holding period (about seven trading sessions) and being flat otherwise.

Best for

  • Normal or rising markets where recurring, calendar-driven cash flows dominate short-term pricing.
  • Periods of steady 401(k)/index-fund inflows and orderly month-end rebalancing.

Avoid when

  • Sharp macro-driven selloffs and fast bear markets, when news overwhelms systematic flows (2002, 2008, 2022 all had negative TOM years).
  • Environments where the effect has been arbitraged thinner — it is well known and has decayed since first documented.

Market behaviour exploited

Recurring, calendar-clustered flow, not news or fundamentals: month-end institutional rebalancing, payroll-driven retirement contributions, dividend reinvestment, and fund "window dressing."

DriverMechanism
Payroll & retirement inflows401(k)/index-fund contributions land around month-end/month-start and are mechanically invested, lifting the broad index.
Institutional rebalancingFunds rebalance to target weights at month-end, concentrating buy pressure into a few sessions.
Dividend reinvestmentA large share of dividends is paid and reinvested near month boundaries.
Window dressingManagers buy winners into the reporting date to improve month-end statements.

Documented by Lakonishok & Smidt (1988) and revisited by McConnell & Xu (2008), who found that historically the entire market risk premium was earned in the turn-of-month window. It is a tendency, not a guarantee, and has weakened as it became widely known.

The rules are unchanged from the original strategy; a pure, date-driven core (turnOfMonthSignalFromBars) now re-expresses the SAME calendar anchors as an as-of signal so the backtest and the live strategy can never drift.

Entry

The 5th-to-last trading day of the monthnthLastTradingDay(year, month, 5).

Implementation: at the latest bar, entry fires on the first session on/after that anchor (previous bar before it, current bar on/after it). Because it keys off the trading calendar (weekends + NYSE holidays skipped), it lands on the correct session every month and fires exactly once. If the anchor day were an unscheduled closure, the next open session fires instead.

Exit

The 3rd trading day of the following monthnthTradingDay(nextYear, nextMonth, 3).

Implementation: the exit anchor is "the 3rd trading day of the month," detected the same way. A position opened on the prior month's 5th-last day is held until that session; the engine turns the standalone exit signal into a round-trip.

Position management

  • One trade per month; flat (in cash) otherwise.
  • No stop-loss, no profit target — the exit is a fixed date. entryPrice/stopLoss/firstTarget/riskPercent/rewardRiskRatio are reported as null (never invented).
  • Holding period: 7 trading sessions in 398 of 401 backtested windows (three were holiday-shortened).

Deterministic, identical in construction to Santa Claus Rally and Russell Rebalancing — it uses the shared seasonalConfidence model. confidenceScore is a setup-quality measure (0–100), not a probability of profit.

ComponentMaxAwarded
seasonalWindow6060 when the window is Active (today is inside it), else 0.
windowProximity40Ramps linearly over the 10 calendar days before entry: 40 × (10 − daysUntilEntry) / 10, clamped to [0, 40]. 0 when far away or already finished.
  • Inside the window → 100 (Exceptional).
  • Approaching (≤10 days) → rises with proximity (e.g. 5 days out → 20).
  • Far / finished → 0.

Bands (library-wide): <70 Weak · 70–79 Good · 80–89 Strong · 90–100 Exceptional.

The API returns ["Turn-of-month seasonal window is active"] while the window is Active, else []. Operationally:

  • It is on/after the 5th-last trading day of the month.
  • Position entered at that session's close in SPY.
  • No stop or target set — the exit is the date, not a price.
  • Exit at the close of the 3rd trading day of the next month.
  • Flat (in cash) outside the window.
  • Treat the historical edge as a tendency, not a guarantee — size accordingly.
  • Structural, not predictive. The edge comes from recurring calendar-driven flow, not from forecasting news or fundamentals.
  • Objective and fully mechanical. Entry/exit are unambiguous calendar rules — no discretion, no indicator tuning.
  • Large, robust sample. 401 trades over 34 years and multiple regimes — far more statistical support than a once-a-year seasonal.
  • Positive expectancy with a modest edge repeated often. 62.6% win rate, 1.85 profit factor; a small ~0.6% edge compounded across ~12 trades/year drove +873.9% over the test.
  • Bounded exposure. In the market ~7 sessions/month, in cash the rest — the strategy sidesteps much of the between-window volatility.
  • Small per-trade edge, decaying. ~0.6% per trade; well known and arbitraged, so future returns may compress.
  • Real tail risk. Worst trade −9.99%, worst year −11.34%, 20.3% max drawdown — a macro shock landing in the window is taken in full (no stop).
  • No adaptive risk control. A fixed-date exit means a bad turn-of-month is held to the exit date.
  • Single-asset, single-window. No diversification within the strategy; frequent (monthly) trading makes it sensitive to commissions/slippage that the close-to-close backtest does not model.
  • Regime dependence. The effect is strongest in calm/rising tapes and weakest exactly when it would help most (crises).
DimensionIdeal
MarketBroad US equities (SPY), normal-to-rising tape where systematic month-end/month-start inflows dominate short-term pricing.
TraderPassive, rules-driven, systematic — someone who will take every monthly window without second-guessing and treat it as a small, repeatable overlay.
TimeframeA recurring ~7-session window each month; a calendar overlay, not a standalone book.
Avoid whenA major macro event lands in the window (FOMC, crisis, liquidity shock); in fast bear markets where flows are swamped by news; or when trading costs/slippage on a monthly cadence erode the ~0.6% edge.
  • **basis enum gap.** The historicalPattern.basis field is "published" or "derived"; there is no "empirical" value in the frozen v1 enum. The pattern is genuinely empirical (computed from our own completed backtest windows), so it is labelled "derived" with the real provenance spelled out in the note.
  • Static snapshot. historical.* and the pattern are baked as of the 2026 windows; they drift as future months complete. The live /api/backtest always recomputes from current data.
  • Close-to-close model. The engine fills at the signal bar's close and models no commissions or slippage — a real cost for a monthly-cadence strategy.
  • Survivorship-free but single-vendor. Prices come from one source (Yahoo). SPY's history begins 1993-01-29, so the backtest starts there.

API examples

Real responses from the live endpoints for this strategy.

Live strategy

GET /api/strategies?id=turn_of_month
GET /api/strategies?id=turn_of_month
{
  "strategies": [
    {
      "id": "turn_of_month",
      "name": "Turn of the Month",
      "category": "Seasonality",
      "asset": "SPY",
      "description": "WHAT IT IS: The Turn of the Month (TOM) effect is a long-standing seasonal tendency…",
      "historical": {
        "yearsTested": 34,
        "winRate": 0.6259,
        "averageReturn": 0.61,
        "bestYear": 27.38,
        "worstYear": -11.34,
        "maxDrawdown": 20.32,
        "averageHoldingPeriod": 7,
        "profitFactor": 1.85,
        "totalTrades": 401,
        "source": "Internal backtest · SPY daily bars (Yahoo Finance) · 401 monthly windows · 1993–2026"
      },
      "historicalPattern": {
        "title": "Turn of the Month — average cumulative return across the held window",
        "xLabel": "Trading days relative to month-end (ME = last trading day; entry T−4, exit T+3)",
        "yLabel": "Average cumulative return (%)",
        "basis": "derived",
        "source": "Internal backtest — 401 completed SPY windows (Yahoo Finance daily closes), 1993–2026",
        "note": "Empirical average path computed across all completed backtest windows on real SPY daily closes… labelled basis:\"derived\" only because the frozen v1 enum offers no \"empirical\" value.",
        "series": [
          { "day": -4, "label": "T−4 (Entry)", "cumulativeReturn": 0.0 },
          { "day": -3, "label": "T−3", "cumulativeReturn": 0.14 },
          { "day": -2, "label": "T−2", "cumulativeReturn": 0.21 },
          { "day": -1, "label": "T−1", "cumulativeReturn": 0.31 },
          { "day": 0, "label": "ME", "cumulativeReturn": 0.27 },
          { "day": 1, "label": "T+1", "cumulativeReturn": 0.47 },
          { "day": 2, "label": "T+2", "cumulativeReturn": 0.55 },
          { "day": 3, "label": "T+3 (Exit)", "cumulativeReturn": 0.61 }
        ]
      },
      "status": "Active",
      "entryDate": "2024-01-25",
      "exitDate": "2024-02-05",
      "daysUntilEntry": 0,
      "entryPrice": null,
      "stopLoss": null,
      "firstTarget": null,
      "riskPercent": null,
      "rewardRiskRatio": null,
      "confidenceScore": 100,
      "confidenceBreakdown": { "seasonalWindow": 60, "windowProximity": 40 },
      "explanation": "SPY is inside its Turn of the Month window (entered 2024-01-25, exits 2024-02-05) — the seasonally strong month-boundary period, rating 100/100 as a setup-quality measure (not a probability of profit). Entry is at the close of 2024-01-25; the strategy sets no price stop or target and exits on the fixed date 2024-02-05.",
      "checklist": ["Turn-of-month seasonal window is active"],
      "score": 70,
      "recommended": true,
      "weight": "medium"
    }
  ]
}

Scanner

GET /api/scan?strategy=turn_of_month

This is a market-wide calendar strategy, not a per-symbol screen, so /api/scan?strategy=turn_of_month returns { "supported": false } with a clear message.

Backtest

GET /api/backtest?strategy=turn_of_month
GET /api/backtest?strategy=turn_of_month
{
  "strategy": "turn_of_month",
  "symbol": "SPY",
  "range": { "requestedFrom": null, "requestedTo": null, "testedFrom": "1993-01-29", "testedTo": "2026-08-04", "barsTested": 8435 },
  "metrics": {
    "totalTrades": 401,
    "winningTrades": 251,
    "losingTrades": 149,
    "winRate": 0.6259,
    "averageReturn": 0.0061,
    "medianReturn": 0.0079,
    "bestTrade": 0.1458,
    "worstTrade": -0.0999,
    "averageHoldingPeriod": 6.99,
    "profitFactor": 1.8503,
    "totalReturn": 8.7385,
    "maxDrawdown": 0.2032
  },
  "sampleTrades": [
    { "entryDate": "2026-06-24", "exitDate": "2026-07-06", "entryPrice": 296.69, "exitPrice": 303.99, "return": 0.0246, "holdingDays": 7 }
  ],
  "openTradeEntryDate": null
}

References

  • Lakonishok, J. & Smidt, S. (1988). Are Seasonal Anomalies Real? A Ninety-Year Perspective. Review of Financial Studies.
  • McConnell, J. J. & Xu, W. (2008). Equity Returns at the Turn of the Month. Financial Analysts Journal — finds the market's historical premium concentrated in the turn-of-month window.
  • Ariel, R. A. (1987). A Monthly Effect in Stock Returns. Journal of Financial Economics.
  • Backtest data: SPY daily OHLCV via Yahoo Finance, 1993–2026 (this repository's market-data layer, lib/market-data/prices.ts).

*Past behaviour does not ensure future results. confidenceScore measures setup quality, not the probability of profit.*