Validate cross-sectional stock selection in multi-symbol strategy code
Cross-sectional stock selection must compare eligible securities at the same decision time. A loop that independently marks every rising stock is a collection of time-series rules, not a top-ranked basket. Build a small aligned panel, calculate the score across the intended axis and assert the exact selected names before calling the code a stock selector.
This guide uses three fictional US stocks and one selection slot. The data, membership dates and returns are synthetic. CoTrading multisymbol strategy checks remain planned or unverified. The deliverable is a dated ranking fixture with per-stock target states, not a historical performance record or an automatically supplied historical constituent universe.
Define rows, columns and the comparison axis
Use a common sequence of completed market sessions. Each security has one price slot per session, with missing observations represented explicitly. A missing row must be aligned as unavailable rather than remove a date and make two adjacent array positions refer to different real intervals. The standalone selector rejects unequal array lengths instead of guessing alignment.
At decision index i, this teaching score is current completed close divided by the previous session’s close minus one. It needs two finite positive observations. Python’s math reference supplies the finite-number check used before division. The short interval is chosen for hand calculation, not as an investment recommendation.
The comparison axis is the securities present at that same i. Sort their eligible scores descending and take one name, with a stable identifier resolving ties. Looking backward through one stock’s own scores would answer a different question. Record the formula and axis together; the word rank alone does not identify them.
Keep membership distinct from available prices
Save when each security becomes eligible for the research universe. The fixture has A and B active from index zero, C from index one. C’s existence in the data dictionary does not authorize selection before that membership date, and a supplied future price does not create a missing historical observation.
A production study needs historical membership or an honest limitation if only current names are available. This guide does not obtain such a history. It checks that a supplied membership contract affects selection as intended. Keep market and currency uniform rather than add unrelated securities simply to obtain enough candidates.
Declare what happens when no stock has enough history. At the first session here, there is no previous price, so no name is selected and each target is zero. That means no selected long target under this fixture; an account with an existing position would still need a separately stated hold-or-exit policy.
Rebuild the first valid cross section
A closes at ten, eleven and twelve. B closes at ten, twelve and eleven. C has no first observation, then twenty and twenty-two. At index one, A’s one-interval return is ten percent and B’s twenty percent. C has no prior price and therefore no score, despite already having a current price.
The one-slot basket selects B. Per-stock targets at index one are A zero, B one and C zero. A positive own return does not make A selected, because B is stronger at that same decision. This row is enough to distinguish a cross-sectional rule from independently buying every positive-return stock.
Do not replace C’s missing prior observation with zero. Division by zero is not a very high momentum score, and converting infinity to a large finite number does not repair the missing history. The valid-input gate must precede calculation. C can become eligible only when its actual required endpoints exist.
Rebuild the next date and universe change
At index two, A returns twelve divided by eleven minus one, approximately 9.090909%. B returns eleven divided by twelve minus one, approximately minus 8.333333%. C returns twenty-two divided by twenty minus one, ten percent. C now has two genuine observations and is the highest score.
The selected sequence across all three dates is no name, B, C. The corresponding A sequence is [0,0,0], B [0,1,0] and C [0,0,1]. They are target states produced after completed-close information, not proof of same-close fills. A later execution layer must schedule and constrain any trades.
Change only C’s active-from index to three. Its prices remain present, but it cannot enter the index-two cross section. A becomes selected there. This counterexample tests universe eligibility, a different mechanism from C lacking a prior observation at index one. Preserve both exclusion reasons rather than merge all absent scores into a generic zero.
Expose the wrong-axis control
The control independently tests whether each stock’s second close exceeds its first. It selects both A and B. Both comparisons are arithmetically true, but the output violates the one-slot cross-sectional rule. A code review that checks only the formula inside the per-stock loop could miss that the final comparison across stocks never occurred.
The error is not fixed by reducing a threshold until one stock happens to remain on a different dataset. The operation itself must construct the same-time score map and select from it. An implementation should make that intermediate map observable so another reviewer can identify whether the wrong axis or the wrong input date caused a discrepancy.
Prove independence from dictionary order
Python’s sorting documentation explains explicit sorting keys. The selector uses score and stable identifier rather than the iteration order of its input dictionary. The fixture enumerates all six orders of the three securities and requires the same cross sections and target values.
Reordering a dictionary can change serialized key order without changing its meaning, so compare the actual mappings and selected names rather than byte strings alone. Conversely, preserving a fixed input order does not prove independence. A hidden tie or a first-match selection bug may appear only when the order is changed.
The script appends a future observation to every stock and requires the first three decisions to remain unchanged. This checks this selector’s causal use of its endpoints. It does not prove that a supplied historic membership file or adjusted price series was free of later revisions; those are upstream evidence requirements.
Run the aligned-panel acceptance file
Download the complete example, save it as R576.py and run python R576.py with Python 3.12 or later. It uses only the standard library and synthetic data. The source is docs/growth/research/round-5/examples/R576.py.
Assertions require the no-name/B/C path, no fabricated C score at index one, all six order-invariance tests, A under delayed C membership and the two-name wrong-axis control. The output preserves scores, selected members and per-stock targets for each date so every decision can be reconstructed independently.
Prepare the real adapter without changing the task
For actual panels, save session identifiers, price availability, membership version, score definition, capacity and tie rule. Reject unresolved alignment rather than silently joining the latest price from another date. A union of dates with missing slots can be useful, but the policy for eligibility at each slot must remain explicit.
Before interpreting a backtest, inspect one new listing, one missing-history date and one universe removal alongside an ordinary cross section. Then attach a separate order and cash model. Interface conformance, correct ranking axis and executable fills are distinct checks. Completion here means another implementation selects the same stock from the same eligible same-time rows, including changes in coverage and membership, without a disguised per-stock timing rule.
The pack also supplies unequal-length arrays and requires an explicit alignment error. It must not truncate the longer stock silently to make the shapes agree. In a real adapter, equal lengths are only the first gate: compare session labels and completion timestamps as well. A table can have three rows for every stock while those rows refer to different dates, which would recreate a mixed-time ranking despite a superficially correct shape.
FAQ
Does every stock with a positive return get selected?
Not under this one-slot ranking. Only the highest eligible same-time score wins.
Can a newly present stock receive a score immediately?
Only when all required historical endpoints and membership conditions are actually available.