Explore more topics
Historical strategy research

Which stocks and dividend facts existed at your decision?

Audit historical stock eligibility with Tesla's dated S&P 500 addition, Target dividend announcements, and a reproducible worksheet for the five-dividend-growers study.

As of 2026-10-13

Jump to a section

TL;DR

Historical eligibility depends on when membership becomes effective and when each input becomes usable. Tesla’s S&P 500 addition was announced in November 2020 but effective December 21. Dividend announcements are another clock: native LastDividend changes on regular ex-dates, and ten annual increases require eleven completed-year values. This worksheet explains those boundaries with Tesla and Target; it does not establish the original dividend study’s full historical universe or input versions.

Tesla's addition was known before Tesla belonged to the index

Animation: a simulated trading day moves from Nov 13 to Dec 21, 2020. The news of Tesla joining the S&P 500 is public from Nov 17, but Tesla is only in the index from Dec 21, leaving about five weeks in between.
Tesla's addition was public from Nov 17, 2020, but Tesla joined the S&P 500 on Dec 21. NexusTrade backtests count it as a member from Dec 21.
Show the table · 6 rows, 5 columns

Scroll sideways to compare all columns. Select a heading to sort.

Nov 13, 2020NoNoNoNo
Nov 17, 2020YesNoNoNo
Dec 11, 2020YesNoNo under this cutoffNo
Dec 14, 2020YesNoYesNo
Dec 18, 2020YesNoYesNo
Dec 21, 2020YesYesYesYes

Actual announced TSLA/AIV change, evaluated at 09:30 America/New_York. Knowledge uses the explicit next-session convention below. This table audits one membership event, not all 500 constituents or the dividend screen.

S&P announced Tesla's addition on November 16, 2020, effective before the December 21 opening session. The company being replaced was still unnamed. Read theNovember announcement.

TheDecember 11 announcement named Apartment Investment and Management (AIV) as the S&P 500 deletion. Both changes took effect before the December 21 open. An announcement-aware model could know the change before then; a universe defined by current membership would still exclude Tesla until that opening session.

These releases give calendar dates, with no exact publication clock in the inspected text. For this daily worksheet, each release becomes usable at the next regular US session open: November 17 and December 14. That is a chosen availability rule, not a measured announcement timestamp. The effective membership date is a separate source fact.

Start with the saved rules in theClaude Code backtesting guide. This worksheet tests the inputs to that example before you interpret its curve.

Target's $1.12 announcement changes a declaration-based input

Show the table · 3 rows, 4 columns

Scroll sideways to compare all columns. Select a heading to sort.

June 11, 2024March 13 release, usable March 14$1.102024 is still in progress
June 12, 2024March 13 release under the chosen cutoff$1.102024 is still in progress
June 13, 2024June 12 release, usable June 13$1.122024 is still in progress

Two actual releases. The next-session availability convention avoids assuming that a date-only release was visible at that day's opening decision. Amounts are declarations per common share, not a simulated portfolio return.

Target'sMarch 13 release declared $1.10, payable June 10 to shareholders of record May 15. Its June 12 release raised the quarterly amount to $1.12, with an August 21 record date and September 10 payment date.

TheJune 12 increase is ($1.12 - $1.10) / $1.10 × 100 = 1.82% using the stated amounts. The release rounds this to 1.8%. A declaration-based feature can use the new amount after its chosen availability cutoff.

The saved NexusTrade strategy uses LastDividend. That indicator changes on the historical regular dividend's ex-date, rather than its declaration, record or payment date. These releases do not supply that ex-date. They establish the announcement timeline; native LastDividend eligibility still needs the dated dividend input rows.

Join each fact to the clock that gives it meaning

A membership interval and a release timestamp serve different roles. The current engine's index lookup includes a dated start and excludes the dated end. Rebalance candidates are scoped to membership at that decision date when membership data is loaded. If it is absent, the engine passes candidates through; the audit should record membership as unknown before interpreting eligibility. Retain the source intervals and the version used in the historical run.

For a source with a precise timestamp, compare it with the decision timestamp in the same timezone and retain the original offset. For a date-only source, choose and record a session-based cutoff before running the screen. A later corrected source also needs its own version and availability time.

Show the table · 7 rows, 3 columns

Scroll sideways to compare all columns. Select a heading to sort.

Public announcement date or timestampWhen could a reader first know this statement?November 16 / December 11 for membership; March 13 / June 12 for dividends.
Chosen usable timestampWhen does this model permit a date-only statement?Next regular session at 09:30 America/New_York; disclose any delay and use the actual session calendar.
Membership effective dateWhen does the stock enter or leave the index?December 21, before the opening session, for the TSLA/AIV change.
Dividend ex-dateWhen does native LastDividend become visible?Required historical input. Unknown in this primary announcement packet.
Record and payment datesWhich shareholder cutoff and cash payment were announced?Retain as separate fields; neither substitutes for the native ex-date.
Source edition and capture timeWhich version did this audit inspect?History edition dated March 4, 2025; sources read October 13, 2026 UTC.
Decision and execution timestampsWhich inputs preceded the decision, and which fill followed it?This worksheet evaluates opening decisions only. It creates no order or execution result.

Eleven yearly values make ten comparisons, but the source edition still matters

Show the table · 12 rows, 3 columns

Scroll sideways to compare all columns. Select a heading to sort.

2013$0.43Baseline
2014$0.52Increase
2015$0.56Increase
2016$0.60Increase
2017$0.62Increase
2018$0.64Increase
2019$0.66Increase
2020$0.68Increase
2021$0.90Increase
2022$1.08Increase
2023$1.10Increase
2024$1.12Current year excluded

Target's retrospective payment history, pages 1 and 2, last updated March 4, 2025. Years follow the December payable dates; amounts use the split-adjusted quarterly column, not annual dividend totals. This is a payment-history arithmetic worksheet, not native ex-date screen output.

TheTarget payment-history PDF gives ten increasing adjacent pairs from 2013 through 2023. There are eleven values because 2013 supplies the first baseline. The 2024 amount is excluded from a completed-year comparison during 2024.

The inspected edition is dated March 4, 2025. It supplies retrospective amounts, but cannot establish the source version available at the January 2024 decision. A numeric streak of ten and an evidenced historical eligibility decision are separate outputs.

Native ConsecutiveIncrease(LastDividend, Year, 10) instead takes the last regular ex-date observation at each completed New York calendar year end, on a consistent split-adjusted basis. A year with no new regular payment can carry the previous value and break an increase through equality. A missing required close also breaks the streak; absent history is not a zero dividend.

Run the timing and comparison checks locally

Run the complete Python timing worksheet

Save this Python worksheet and run it locally. It uses only the source values printed above and Python's standard library. It makes no network, market-data or backtest calls. Its output reports ten retrospective increases while leaving the native ex-date screen and original 2014 input audit unverified.

The assertions check an announced but not effective member, the effective deletion date, the dividend before and after the chosen cutoff, an excluded current year, equality, a missing close, and a completed dividend cut. Those boundary cases tell you which row or clock would change the result.

Python
View complete example
"""Reproduce the page's source-timing checks. No API, market data or backtest calls."""

import json
from datetime import date
from decimal import Decimal

# These usable dates are explicit daily-screen conventions, not measured release times.
TSLA_ANNOUNCEMENT_USABLE = date(2020, 11, 17)
INDEX_CHANGE_EFFECTIVE = date(2020, 12, 21)
AIV_REMOVAL_USABLE = date(2020, 12, 14)
DIVIDEND_ANNOUNCEMENTS = [
    (date(2024, 3, 14), Decimal("1.10")),
    (date(2024, 6, 13), Decimal("1.12")),
]

# Target's dated retrospective history: last listed December quarterly amounts.
# Each is an observed source value, not a substitute for native ex-date input rows.
PAYMENT_CLOSES = {
    2013: Decimal("0.43"), 2014: Decimal("0.52"),
    2015: Decimal("0.56"), 2016: Decimal("0.60"),
    2017: Decimal("0.62"), 2018: Decimal("0.64"),
    2019: Decimal("0.66"), 2020: Decimal("0.68"),
    2021: Decimal("0.90"), 2022: Decimal("1.08"),
    2023: Decimal("1.10"), 2024: Decimal("1.12"),
}
HISTORY_EDITION_DATE = date(2025, 3, 4)


def index_decision(day):
    """Audit only the named TSLA/AIV change, not the full index population."""
    return {
        "date": day.isoformat(),
        "tesla_addition_known_under_daily_convention": day >= TSLA_ANNOUNCEMENT_USABLE,
        "tesla_member_for_this_change": day >= INDEX_CHANGE_EFFECTIVE,
        "aiv_removal_known_under_daily_convention": day >= AIV_REMOVAL_USABLE,
        "aiv_removed_for_this_change": day >= INDEX_CHANGE_EFFECTIVE,
    }


def latest_declared_amount(day):
    visible = [amount for usable, amount in DIVIDEND_ANNOUNCEMENTS if usable <= day]
    return str(visible[-1]) if visible else None


def trailing_increases(closes, decision_year, lookback=10):
    """Completed-year arithmetic with the native relative-equality tolerance."""
    count = 0
    for year in range(decision_year - 1, decision_year - lookback - 1, -1):
        current, previous = closes.get(year), closes.get(year - 1)
        if current is None or previous is None:
            break
        tolerance = Decimal("0.000001") * max(abs(current), abs(previous))
        if current - previous <= tolerance:
            break
        count += 1
    return count


def run_checks():
    assert index_decision(date(2020, 11, 13))["tesla_addition_known_under_daily_convention"] is False
    assert index_decision(date(2020, 11, 17))["tesla_addition_known_under_daily_convention"] is True
    assert index_decision(date(2020, 11, 17))["tesla_member_for_this_change"] is False
    assert index_decision(date(2020, 12, 11))["aiv_removal_known_under_daily_convention"] is False
    assert index_decision(date(2020, 12, 14))["aiv_removal_known_under_daily_convention"] is True
    assert index_decision(date(2020, 12, 14))["aiv_removed_for_this_change"] is False
    assert index_decision(date(2020, 12, 18))["aiv_removed_for_this_change"] is False
    assert index_decision(date(2020, 12, 21))["tesla_member_for_this_change"] is True
    assert index_decision(date(2020, 12, 21))["aiv_removed_for_this_change"] is True
    assert latest_declared_amount(date(2024, 6, 11)) == "1.10"
    assert latest_declared_amount(date(2024, 6, 12)) == "1.10"
    assert latest_declared_amount(date(2024, 6, 13)) == "1.12"
    assert trailing_increases(PAYMENT_CLOSES, 2024) == 10
    altered = {**PAYMENT_CLOSES, 2024: Decimal("0.01")}
    assert trailing_increases(altered, 2024) == 10  # Current year stays excluded.
    assert trailing_increases(altered, 2025) == 0   # Completed cut breaks the streak.
    equal = {**PAYMENT_CLOSES, 2023: Decimal("1.08")}
    assert trailing_increases(equal, 2024) == 0
    missing = {**PAYMENT_CLOSES, 2022: None}
    assert trailing_increases(missing, 2024) == 0
    assert HISTORY_EDITION_DATE > date(2024, 1, 2)
    raise_percent = (Decimal("1.12") - Decimal("1.10")) / Decimal("1.10") * 100
    print(json.dumps({
        "index_decisions": [index_decision(date.fromisoformat(day)) for day in ["2020-11-13", "2020-11-17", "2020-12-11", "2020-12-14", "2020-12-18", "2020-12-21"]],
        "declared_amount_before": latest_declared_amount(date(2024, 6, 11)),
        "declared_amount_after": latest_declared_amount(date(2024, 6, 13)),
        "raise_percent_from_rounded_source_amounts": str(raise_percent.quantize(Decimal("0.01"))),
        "retrospective_completed_year_increases": trailing_increases(PAYMENT_CLOSES, 2024),
        "history_edition_available_on_2024_01_02": False,
        "native_ex_date_screen_reproduced": False,
        "original_2014_backtest_point_in_time_certified": False,
    }, indent=2))


if __name__ == "__main__":
    run_checks()

Apply the audit to the five-dividend-growers study

Thesaved configuration starts with the S&P 500 universe, requires ten consecutive completed-year increases in LastDividend, selects the five highest market caps, and equally weights them. It starts with $10,000, permits fractional shares, reinvests dividends and uses market orders. Its rebalance trigger checks the initial-value condition or at least 30 days since a filled buy; it is not a calendar-month schedule.

Keep therecorded study and curves as their existing historical evidence. These Tesla and Target worksheets explain how to inspect inputs; they do not rerun, alter or certify the original 2014 simulation. The missing original input-vintage audit remains visible below.

Show the table · 6 rows, 3 columns

Scroll sideways to compare all columns. Select a heading to sort.

S&P 500 universeMembership intervals for all candidates, including exits; effective dates, primary announcements and the provider snapshot/version.The TSLA/AIV event checks one boundary. Original full historical universe provenance is not established here.
Ten completed-year increasesFor a 2014 decision: 2003 baseline through 2013. For a 2025 decision: 2014 baseline through 2024. Preserve ex-dates, regular/special classification, values, split basis and availability/version.The Target payment worksheet demonstrates ten comparisons. It lacks the native ex-date packet and does not prove original eligibility.
Highest market cap, five stocksThe historical price, share-count inputs, their availability and source version, plus the ranking across every eligible stock at the decision.A saved ranking rule does not establish the vintages behind the recorded ranking.
Equal weights and market ordersDecision timestamp, input cutoff, requested weights, fees, fractional-share behavior and the actual subsequent fill timestamps/prices.No fills or performance estimates are created by this worksheet.
Initial-value or 30-day triggerThe prior filled-buy state and decision time for each rebalance.Preserve the actual saved trigger and order history when matching a run.
Incomplete or revised dataAn explicit unknown state and the policy for missing history, corrections, delisted stocks and unavailable inputs.Report unresolved eligibility separately from a pass or a fail.

Check the dataset’s universe and availability clock

Massive'sETF Global constituents documentation describes historical ETF holdings from April 3, 2017 in a separate paid expansion plan. It retains both effective_date and processed_date; issuer reporting delays can put those dates weeks apart. An ETF holdings record and an official index membership record have different meanings. This dataset's stated range alone cannot establish the 2014 S&P 500 universe in the original study.

Use thedata-provenance worksheet to separate what a vendor documents, what a platform actually retained and what the saved run certifies. A missing vintage audit leaves the original study unresolved. That disclosure alone does not establish inaccurate or low-quality vendor data, or competitor superiority; a source or consumer defect needs separate evidence.

Keep a decision ledger beside the curve

For each rebalance, freeze the decision timestamp and source snapshot first. Apply membership intervals, retain only inputs allowed by their availability policy, then calculate the completed-period streak. Rank the resulting eligible set, record each inclusion or exclusion reason, and retain the subsequent order and fill history separately.

A useful audit row includes symbol, decision time, membership start/end, source URL or dataset version, availability time, ex-date, dividend type, split basis, completed-year values, streak, market-cap rank and the final eligibility status. Record unknowns explicitly so another reader can reproduce both the selected stocks and the rejected candidates.

Use theClaude Code guide to inspect the saved strategy and recorded result. Keep this ledger with that research record before proposing a new historical test.

Continue exploring

Discussion

Sign in or create a free account to join the discussion.

No comments yet.

Aurora · AI research assistant

Try this in Aurora

Edit the request, then open it in Aurora. You choose when to send it.