Skip to content

Learn

Stock Factor Research

Build an earnings-yield stock factor using filing availability dates

An earnings-yield stock factor needs a history of what was available, not just a history of accounting periods. Divide a consistently defined earnings measure by contemporaneous equity market value, then select stocks only from facts available by each decision cutoff. The deliverable is a monthly portfolio whose financial inputs, membership and cash movements can all be reconstructed.

The following US-dollar example uses invented companies, filings and prices. It demonstrates an availability-aware selection rule and two self-financed trades. It is not a download of SEC data or a historical factor backtest. CoTrading fundamental research features remain planned or unverified, so the method is presented as an independent research contract.

Acquire facts with their original context

The SEC EDGAR API documentation describes company submissions and XBRL facts, including distinctions between concepts, units and reporting periods. Preserve the accession identifier, form, reporting interval, unit, fact value, filing timestamp and the timestamp at which your pipeline could use it. A company identifier must also map to the correct historical traded security.

Choose one numerator definition before ranking. A trailing annual profit measure should cover four non-overlapping quarters or an equivalent annual interval, with a written treatment of later year-to-date disclosures. Do not add a nine-month cumulative profit to three separate quarters already included in that cumulative figure. Verify whether the chosen concept is attributable to common shareholders and whether preferred claims require adjustment.

Match the denominator’s economic unit. Company-wide earnings divided by one share class’s incomplete capitalization can exaggerate yield. Record price date, share-count date, eligible share classes and currency. If a defensible historical equity market value cannot be constructed, mark the stock unavailable rather than substituting today’s capitalization across past dates. The example avoids those complexities by explicitly supplying a fictional capitalization of 100 for each company.

Define the monthly availability rule

Use a fixed month-end decision cutoff and execute later, at a designated next-session opening price. For date-only financial availability, adopt a conservative next-session convention or obtain an actual timestamp; a date alone does not establish that the filing was usable before that day’s close. The fixture uses availability dates already determined by such a process.

At each cutoff, filter out unavailable facts first. Among the remaining versions, select the latest reporting period and then the latest available revision for that same period. Preserve the previous version so a January decision can still be reproduced after a February correction. Picking the most recently downloaded value would make an archival data collection behave as if it had always known the correction.

Exclude nonpositive profits and missing or nonpositive market capitalization in this strategy. This is a predeclared eligibility rule, not a universal recommendation about loss-making stocks. Keep separate statuses for losses, absent earnings, invalid capitalization and insufficient history. Sort positive earnings yields descending, with a stable security identifier breaking ties, then choose the predeclared number of names.

Rebuild two monthly decisions

Invented A reports annual income of five, usable on January 20. B reports eight, usable on January 22. C reports minus four, usable on January 25. All refer to a December 31 period, and each supplied capitalization is 100. At the January 30 cutoff, A scores 5%, B 8%, and C is excluded by the loss rule. The one-slot teaching portfolio selects B.

On February 10, a corrected version reduces B’s same-period income to two. At the February 27 cutoff, B’s yield is therefore 2%, while A remains 5%; A replaces B. A different A fact has income of 100 but is unavailable until April 20 and belongs to a later period. Neither January nor February may use that tempting future value.

The example deliberately holds capitalization constant to isolate revision timing. A real monthly study would recompute it using that month’s permitted price and share information. Otherwise a change in earnings yield could be incorrectly attributed to accounting news when it actually came from price movement, or a valid price change might be omitted from the rank.

Turn membership into a cash bridge

Start with 1,000. At the opening after the first decision, buy ninety B shares at ten and pay a fictional fee of one, leaving ninety-nine cash. At the later rebalance open, sell those shares at eleven, receiving 990 less one fee. Cash becomes 1,088. Buy one hundred A shares at ten and pay one more fee, leaving eighty-seven.

Mark A at a later supplied price of 10.5. Shares are worth 1,050, so marked equity is 1,137. Total fees are three. This final value still includes an open position; it is not a terminal-liquidation cash balance. A forced sale would need another price and another cost entry. The fixture’s attractive price path was invented and supplies no evidence about actual earnings-yield returns.

A real basket needs quantity rounding, simultaneous cash allocation and rejected-order treatment. Freeze these independently of the score. Changing the number of names or position sizing after seeing a revised ranking changes the strategy under evaluation. Retain the before-and-after portfolio and the reason for every departure from the target basket.

Execute the availability checks

The standard-library script docs/growth/research/round-5/examples/R562.py keeps financial observations as separate immutable rows. The central operation is:

known = [fact for fact in facts
         if fact['symbol'] == symbol and fact['available'] <= cutoff]
latest = max(known, key=lambda fact: (fact['period'], fact['available']))

The full script handles an empty eligible set before calling max, applies the profit rule, ranks candidates and checks cash. Run python docs/growth/research/round-5/examples/R562.py. Assertions require B in January, A in February, B’s respective scores of 8% and 2%, exclusion of C, no selection before the first availability date, cash of eighty-seven and marked equity of 1,137.

Use revisions as the boundary test

The failure case is a revision backfilled into January. Replacing every B observation with the February value would select A a month early. That is a timing error even if every arithmetic division is correct. Conversely, retaining B’s original eight forever after February would ignore information actually available then. Version selection must move forward with the cutoff, not freeze one universal latest record.

Inspect archived source rows whenever a provider updates fundamentals. An API that returns a calendar-aligned latest fact is not automatically a historical availability database. Review duplicate contexts, amended filings and fiscal-year changes before adding observations. Accept the package only when an independent reader can identify the exact source version behind each monthly score and reconcile each replacement trade. Update the study when its numerator, availability convention, security mapping or capital denominator changes, with a new version rather than a silent overwrite.

Download the complete example. Save it as R562.py and run python R562.py with Python 3.12 or later. It uses only the standard library and synthetic data.

Keep an unresolved-fact queue

Do not force every downloaded item into the ranking panel immediately. Place facts with conflicting units, overlapping durations or unexplained restatements in a separate queue carrying the original accession and the unresolved reason. The portfolio uses the last eligible fact only if the approved policy permits it and its age remains acceptable; otherwise the stock is unavailable. This queue makes data quality a visible dependency instead of an undocumented judgment inside a score calculation.

Review the queue before the next scheduled monthly build. Record whether an item was clarified by a source filing, rejected as the wrong concept or retained as unresolved. An exclusion that later disappears should create a new panel version with a reason, not a silent modification of the archived January selection. Also compare the number of positive-profit candidates, excluded losses and missing denominators each month. A sudden collapse in coverage can reveal a mapping or taxonomy problem that would otherwise look like an economically meaningful change in the factor universe.

If a selection cannot be traced to its original filing version, stop at that evidence gap rather than substitute the latest downloaded value.

FAQ

Why exclude negative earnings?

That rule makes this particular positive-yield strategy deterministic. A separate loss-aware strategy could be researched with different eligibility rules.

Does the SEC API alone give a complete backtest dataset?

No. Historical security mapping, usable timestamps, matched prices, capitalization and execution assumptions still require explicit work.