Evaluate a stock factor from daily scores to net group portfolios
A factor evaluation is complete only when its statistical groups and its investable basket can be reconstructed separately. Start with the date-by-stock score panel, pair each score with a specified future return, freeze group members, and then rebuild the chosen group’s orders and cash. A group chart alone leaves the execution question unanswered.
This guide produces an eligible-row table, a deterministic grouping table and a small cash ledger. Every number below is synthetic. The calculation checks mechanics rather than historical profitability. Use one equity market and currency per run. CoTrading factor workspaces remain planned or unverified; the procedure does not depend on an available product feature.
Freeze the observation and trading clocks
Give every row a stable security identifier, score timestamp, availability timestamp, eligibility status, entry price timestamp and exit price timestamp. For a daily close-derived score, the return label should begin at a later executable price, such as the following session’s open, rather than at the close that supplied the score. Store the actual dates instead of relying on an ambiguous column called tomorrow.
Choose the price treatment before joining. A total-return label and a raw opening-price execution ledger need a documented bridge for distributions and corporate actions. Otherwise apparently different factor and portfolio returns can result from different price definitions. Preserve exclusions for missing prices, unavailable scores and securities outside the frozen universe. Do not silently turn an unavailable exit observation into a zero return.
Set a minimum of five valid paired stocks for each information-coefficient observation. This is an operational minimum for this exercise, not a claim that five stocks provide reliable statistical evidence. A constant score or return vector makes correlation undefined; report that status rather than treating it as zero. Daily coefficients also need a coverage count so that changes in sample composition remain visible.
Make the cross section reproducible
The six invented stocks A through F have scores one through six. Their next specified open-to-open returns are one, two, three, four, five and six percent. Sort ascending by score and then by stable identifier. Divide this fixed six-stock fixture into three groups with two members each. Group one contains A and B, group two C and D, and group three E and F.
The equal-weight group returns are 1.5%, 3.5% and 5.5%. Higher values are preferred in this example; the intended long basket is therefore E and F. A low-value-preferred factor would require a different mapping, fixed before observing returns. Never relabel whichever group performed best as the preferred group after the fact.
Compute the correlation between score ranks and return ranks. Python’s statistics reference documents the arithmetic and correlation tools used in the standalone calculation. The example explicitly computes average ranks for ties, then applies ordinary correlation to those ranks. Here both rank vectors are identical, so the coefficient is one. This perfect synthetic ordering does not estimate a future effect size or justify deployment.
For larger cross sections, freeze a general capacity rule rather than copying the fixture’s two-stock slices. State how remainder members are assigned, how boundary ties are resolved and what happens when the valid count is smaller than the intended group count. Reordering input rows must not alter membership. Keep the complete member list beside the return so another implementation can reproduce it.
Convert the preferred group into actual quantities
Allocate a separate synthetic account of 1,000 currency units. Both E and F have an entry open of 10. Buy forty shares of each and charge a fictional commission of one unit per order. Total purchase notional is 800 and entry fees total two, leaving 198 in cash. This deliberately holds a cash reserve rather than assuming the full statistical group weight is executable.
At the next designated open, E trades at 10.5 and F at 10.6. Selling forty E shares produces 420 before its one-unit fee; selling forty F shares produces 424 before its one-unit fee. Final cash is 198 plus 419 plus 423, or 1,040. There are no remaining shares, and net account return is 4%.
The 5.5% group return and 4% account return answer different questions. The group gives each selected security half of a fully invested analytical basket. The account initially invests eighty percent of capital and pays four units across its round trip. Its gross account gain is forty-four and its net gain is forty. This bridge is required before using a group chart to describe money that could have been invested.
Reproduce the small calculation
The repository example docs/growth/research/round-5/examples/R561.py runs with Python’s standard library. Its core grouping operation is intentionally visible:
ordered = sorted(valid, key=lambda row: (row['score'], row['symbol']))
groups = [ordered[i:i + 2] for i in range(0, len(ordered), 2)]
group_returns = [mean(row['return'] for row in group) for group in groups]
Run python docs/growth/research/round-5/examples/R561.py from the repository root. Assertions check the exact members, the rank coefficient, the top-group return, final cash and the insufficient-sample status. The output contains all four orders, so a reviewer can recompute cash without trusting a single summary statistic. Floating-point comparisons use tolerances for this teaching fixture.
Test a failure with a different mechanism
Remove E and F from the paired dataset. Only four valid stocks remain, so the evaluator returns skip with a count of four. It does not calculate an apparently informative daily coefficient from a different, undersized population. This is a coverage failure; it differs from transaction costs reducing an otherwise valid portfolio result.
Decide separately what an existing account does on a skipped research date. Skipping the statistical observation does not automatically mean liquidating live holdings. A production research contract must define whether the account holds previous positions, cancels new entries or exits at a later available bar. Give those decisions their own order records; do not invent a missing group return to keep the curve continuous.
Extend the ledger before extending the claim
For repeated dates, store prior quantities, sale proceeds, buy requirements, commissions, remaining cash and marked positions. Membership changes produce turnover; repeated membership does not necessarily require a complete round trip. Preserve rejected orders and unfilled targets. Different markets require separate eligibility and execution assumptions, and a Hong Kong study needs its own panel rather than an implied built-in dataset.
Historical universe membership is another independent requirement. A current constituent list backfilled over earlier years retains survivor bias even if the rank calculations are flawless. Save the universe source and effective date. Before accepting the package, trace one date from raw score to label, group, order and account equity; then trace one skipped date. Update the study when data versions, universe definitions, cost schedules or timing conventions change, keeping the previous artifacts available for comparison.
Download the complete example. Save it as R561.py and run python R561.py with Python 3.12 or later. It uses only the standard library and synthetic data.
Review membership changes without rewriting history
Treat a research rerun as a versioned comparison. For one chosen date, join old and new rows by security identifier and classify differences as input revision, eligibility change, score change or execution change. A changed next-period return can alter a group result without changing a single group member. A changed entry price can alter share quantity and account exposure while leaving the statistical score untouched. Keeping these categories separate prevents a revised curve from being described as improved selection when only the accounting inputs changed.
Ask the reviewer to reconstruct the preferred group’s gross gain from its two return labels and reconstruct account profit from its four cash events. Then ask what is not demonstrated: this fixture has no multi-date uncertainty estimate, delisted-stock history or capacity analysis. Archive those questions as outstanding research dependencies. Acceptance of the arithmetic package permits a larger study to begin; it does not convert a six-stock example into evidence that the factor works across securities, regimes or future trading costs.
The script additionally checks constant-score correlation as undefined and confirms that reversing input order leaves group members unchanged. Record statistical and account acceptance separately; neither substitutes for the other.
FAQ
Does a coefficient of one mean this factor will make money?
No. It describes the ordering of six deliberately constructed observations, while the account result also depends on exposure, fills and costs.
Can I use the group return as net performance?
Only after a separate execution ledger reconciles quantities, cash, costs and valuation. This example explicitly shows why they differ.