Skip to content

Learn

Stock Factor Research

How to winsorize stock factors and check changes in ranks and trades

Winsorizing a stock factor changes values, but it does not necessarily change which stocks a portfolio owns. Trace the transformation through the ranking boundary and the actual quantity rule before interpreting a smoother distribution as a better strategy. The deliverable is a score-change table connected to member changes, orders, fees and net equity.

This guide uses two synthetic five-stock fixtures in one fictional US market. All prices and account results are invented. CoTrading factor-transformation auditing remains planned or unverified. The example implements a declared order-statistic cap with the standard library; it does not claim to reproduce a library’s undocumented default or to identify an optimal tail threshold.

Define the transformation before evaluating returns

Preserve original scores and create a new transformed column. In this exercise, sort the valid current cross section and replace the single largest value with the second-largest value. Values below that ceiling are unchanged. The cap uses only the current permitted scores and no later price, subsequent score or full-history percentile.

The SciPy winsorize documentation describes replacing tail values and exposes choices about limits and how counts are handled. It is a useful definition reference. The downloadable script does not import SciPy: its one-largest-value rule is explicitly implemented, avoiding ambiguity about interpolation, truncation or masked-array behavior.

Choose the valid universe first. Missing or nonfinite scores should not silently become the smallest or largest value. For a real panel, declare a minimum cross-section count and what happens when the cap cannot be estimated. A percentile derived from a different universe or from later rows changes the research input even if the formula has the same name.

Distinguish an incorrect observation from a true extreme

An extreme score can come from a unit error, an almost-zero denominator or a genuine economic event. Capping is not a substitute for checking provenance. If a currency conversion is wrong, repair that input and retain the correction record; treating it as a routine tail observation would conceal a data defect.

Conversely, do not delete an extreme merely because its later return is inconvenient. The cap is a predetermined sensitivity rule, not permission to revise history until a performance curve looks stable. Keep both raw and transformed versions so their effects can be explained. The comparison asks what this specific rule changes, not whether all outliers are false.

Record the ceiling, eligible count, transformed identifiers and transformation version for every decision. A cap computed from the future full panel can leak information even though the resulting values look reasonable. Rebuilding the ceiling from the saved decision cross section is an essential acceptance check before examining any portfolio result.

Observe a value change without a member change

The first fixture has A, B, C, D and E scores of one, two, three, four and one hundred. The current ceiling is four, so E changes from one hundred to four. Select the top two with equal fixed quantities, using descending score and ascending identifier for ties. Raw members are E and D; capped members are D and E.

The order changes because the top scores tie, but the member set is identical. With equal fixed quantities and no scheduled resizing, this transformation alone generates no member-replacement order. A test that compares list positions rather than holdings could falsely report turnover. Preserve both ranking order and actual target quantities to distinguish these concepts.

This invariance would not automatically hold under score-proportional sizing. Changing one hundred to four changes relative magnitudes, even with the same selected names. Such a sizing comparison requires its own quantity calculation. The current fixture deliberately fixes quantities so it tests membership rather than silently introducing a different allocation mechanism.

Trigger a boundary change with the same cap rule

The second fixture uses X at one hundred, A at four, B at three, C at two and D at one. Again cap only the largest value at the second-largest, four. Now select one stock. Raw sorting chooses X. After capping, A and X tie at four; the declared ascending identifier rule chooses A.

The cap has not reversed two unequal transformed scores. It has created a tie across the one-slot boundary, after which a separate tie policy determines the member. Python’s sorting reference documents the multikey sorting used here. Do not describe the new member as having a strictly stronger capped score when its advantage is actually the deterministic tie key.

Both fixtures were specified before the later marks. They use the same one-largest-value cap with different stated portfolio capacities. They are mechanism tests, not a search over thresholds for the best outcome. If comparing real strategies, freeze one construction for the intended experiment and record other fixtures only as acceptance tests.

Follow the replacement into cash

For the second fixture, begin with eighty X shares at a reference price of ten and 200 cash, for starting equity 1,000. The raw rule retains X without an order. A later supplied X mark of 10.2 gives equity 1,016. Earlier acquisition fees are outside this single-rebalance comparison and are not fabricated.

The capped rule sells eighty X at ten, paying one fictional fee, and buys eighty A at ten, paying another. Cash becomes 198. At A’s later supplied mark of 10.1, holdings are worth 808 and equity is 1,006. The ten-unit difference includes eight units of later price-value difference and two of replacement fees.

The capped account’s lower value does not establish that winsorization is bad. The raw account’s higher value does not justify selecting the rule retrospectively either. These invented marks simply make the economic consequence inspectable. An order ledger is stronger evidence of the mechanism than a before-and-after histogram, but it is still not real-market performance evidence.

Run the independent transformation pack

Download the complete example, save it as R565.py and run python R565.py with Python 3.12 or later. The standard-library source is docs/growth/research/round-5/examples/R565.py. No prices or external services are fetched.

Assertions check the ceiling of four, unchanged two-member holdings in the first fixture, X-to-A selection in the one-slot fixture, exact replacement orders and equities of 1,016 and 1,006. Review the cap and the tie key together. A function that returns the expected final equity while using the wrong members could pass a weak cash-only test.

The account checks also require an empty raw-order list, the exact sell-X/buy-A pair with eighty shares at ten and one-unit fees, raw/capped cash 200/198 and fee totals zero/two. Deleting the returned order field is caught even when the equity arithmetic is unchanged. The fee total is reconciled directly from the order records, so a missing transaction artifact cannot pass on terminal equity alone.

Package the result for review

Include raw scores, transformed scores, cap provenance, selection flags, target quantities, actual orders and cost totals. Classify each changed score as having no target effect, a quantity effect or a member effect. These categories can coexist in a larger basket, so a single count of capped values is not a sufficient summary.

Revalidate after universe changes, denominator corrections, transformation revisions or quantity-rule changes. Keep one unchanged-membership test and one boundary-tie test so later refactors cannot confuse numeric smoothing with executed turnover. Acceptance means that every claimed economic effect can be traced to a permitted current-cross-section transformation and an explicit portfolio decision, with no assertion that tail clipping has improved expected returns.

Compare holdings by identifier, not sorted position

Build the order difference from security-to-quantity mappings. In the unchanged-membership fixture, E and D swap list positions after capping, but each still has the same target quantity. Pairing the first old row with the first new row could manufacture an exit and entry even though nothing economic changed. The holdings map avoids that artificial turnover.

At the one-slot boundary, record the original score, capped score and final tie key for both X and A. A reviewer can then establish that X was not deleted as a bad observation: it remained eligible, received the declared ceiling and lost an explicit tie. If a real data correction instead removes X, use a separate exclusion reason. These paths can produce the same final holding while requiring different explanations and future update behavior. Retaining both cases prevents the transformation report from becoming a catch-all label for unrelated data cleaning and execution changes.

FAQ

Must a capped score create a trade?

No. Equal-quantity holdings can remain unchanged even when a large value changes substantially.

Can I choose the ceiling after seeing returns?

That would be a different, outcome-informed experiment rather than this frozen sensitivity rule.