Skip to content

Learn

Stock Screening

Rank screened stocks after applying the hard filters

Rank stock screener results only after applying the conditions that every candidate must satisfy. Eligibility answers whether a stock can enter the research queue. Ranking answers which eligible stock deserves attention first when time is limited. Combining those questions into one score permits an attractive preference to compensate for a condition the researcher intended to be mandatory.

The example below allocates two research slots among fictional stocks. It calculates a transparent preference score, changes the weights, and verifies that a rejected stock never returns. The numbers are synthetic research inputs, not evidence that a factor predicts returns. CoTrading’s corresponding workflow remains planned or unverified; this is an independent method with a runnable standard-library fixture.

Define eligibility separately from preference

Start with the dated universe and a list of mandatory requirements. Our fixture requires average daily turnover of at least two million units of one specified currency over one agreed window. We use the short field turnover, expressed in millions, so the stored threshold is 2. In actual work, confirm whether the provider reports traded value or traded shares. A currency threshold cannot be applied directly to share volume.

Then define preferences within the eligible set: a smaller valuation rank and a smaller growth rank are preferable. These ranks have already been calculated from comparable observations in the example. They are not raw PE and percentage growth values. In a real screen, preserve the ranking population and direction for each metric, because rank 1 among three companies does not mean the same thing as rank 1 among several hundred.

Do not use a preference as an undeclared gate. If missing growth should exclude a stock entirely, state that in eligibility. Here a missing preference leaves an otherwise eligible stock in a pending scoring queue. That distinction allows the researcher to see whether the bottleneck is market eligibility or incomplete ranking information.

Prepare a reconstructable scoring table

The synthetic inputs are deliberately arranged to create competing preferences. AAA is stronger on value, BBB is stronger on growth, and DDD is balanced. CCC has excellent preference ranks but insufficient turnover. EEE passes turnover but lacks the valuation rank.

StockTurnover, millionsValue rankGrowth rankInitial disposition
AAA313eligible and scored
BBB431eligible and scored
CCC111rejected by turnover
DDD322eligible and scored
EEE3missing1eligible, scoring pending

Choose a score direction and print it beside the formula. This example uses score = w × value_rank + (1 − w) × growth_rank; lower is better. With a value weight of 0.75, AAA scores 1.50, DDD 2.00 and BBB 2.50. Keeping both contributions makes a total recoverable. AAA’s value contribution is 0.75 and its growth contribution is 0.75, producing the displayed 1.50.

An unweighted sum of raw metrics would not be equivalent. A large currency-valued metric could dominate a small ratio solely because their units differ. Rank conversion is one possible normalization, but it discards distances between observations. Document that tradeoff rather than presenting ranking as a universal solution to comparability.

Execute the eligible-set calculation

The core operation first selects the eligible rows, then computes scores only when both preferences are present. It never scores CCC and hopes that its liquidity penalty will be large enough. This ordering is what enforces the mandatory condition.

eligible = [r for r in rows if r["turnover"] >= 2]
scored, pending = [], []
for row in eligible:
    if row["value_rank"] is None or row["growth_rank"] is None:
        pending.append(row["symbol"])
        continue
    score = weight * D(row["value_rank"]) + (1-weight) * D(row["growth_rank"])
    scored.append((score, row["symbol"]))
ordered = sorted(scored)

The full fixture is docs/growth/research/round-5/examples/R518.py; run it with Python. Actual output with weight 0.75 is AAA, DDD, BBB, with scores 1.50, 2.00, 2.50 and EEE pending. Taking the first two yields AAA and DDD. The Python sorting guide explains key-based and stable sorting; this fixture uses the symbol as an explicit secondary ordering key so results do not depend on provider row order.

Download the complete example. Use Python 3.12+ with the standard library only; all inputs are synthetic. After downloading, run python R518.py.

The fixture uses decimal weights constructed from strings. Python’s decimal reference documents that representation. Retain unrounded internal scores and round only for display. Otherwise two genuinely different scores may be treated as tied merely because both print with two decimal places.

Test changes in preference without changing eligibility

Run a second configuration with value weight 0.25 and growth weight 0.75. BBB now scores 1.50, DDD 2.00 and AAA 2.50. The two research slots become BBB and DDD. CCC still cannot enter, and EEE remains pending. This test checks the intended independence: a change in preference can change the order among scored eligible stocks, while the same hard rule keeps the rejected set fixed.

The counterexample is a composite score that rewards CCC’s two first-place ranks enough to overwhelm a weak turnover penalty. Such a score would put an explicitly ineligible company into the shortlist. The fixture asserts that CCC is absent after the weight change. Inspecting only the top score would miss that failure because the arithmetic could be perfectly correct while the eligibility policy was wrong.

Also test weight 0.50. AAA, BBB and DDD all score 2.00. The fixture orders them AAA, BBB, DDD by symbol. That ordering is a reproducibility rule, not a claim that AAA is economically better. If two research slots cut across a tie, publish the tie and the tie-break rule. Another reasonable policy is to review all tied stocks or choose fewer names; decide before seeing their identities.

Deliver an order that can be maintained

Save the rejected table with reasons, the eligible but pending table, the complete scored table and the truncated research list. Include the universe version, hard-rule version, preference definitions, weights, scoring direction and tie policy. The truncated list alone cannot show why a name was omitted. A reviewer should be able to distinguish failure of the hard condition from a rank just below the capacity cutoff.

When new data arrive, rerun eligibility before scoring. A stock that loses eligibility should not remain because it was previously near the top. When weights change, keep the data snapshot fixed during the comparison so the reason for reordering is visible. When missing data are completed, recalculate any ranks whose comparison population changes instead of inserting a new row into an obsolete ordering.

Use separate market or peer comparisons when field definitions are not comparable. This procedure schedules research; it neither chooses portfolio weights nor validates a trading strategy. Its acceptance test is narrower and useful: every selected stock passes the hard rules, every score can be reconstructed, pending observations remain visible, and repeated runs with identical inputs produce the same research order.

Check the cutoff as a capacity decision

Before taking the first two stocks, display the complete ordered eligible set and the pending count. Confirm that two is a research-capacity limit rather than a hidden score threshold. If capacity changes to three, BBB joins the value-first research list without any eligibility change. If capacity stays at two but a tie policy changes, record that policy revision separately from a weight revision.

Keep a contribution ledger for each row, including the rank values, weights and unrounded score. Have a reviewer reconstruct one value-first score and one growth-first score directly from that ledger. Also verify that CCC appears in the rejection table and EEE in the pending table, rather than disappearing from the report. This small reconciliation checks both numerical transparency and complete coverage of the original universe.

When reporting a weight comparison, show the same stocks side by side. Changing the comparison population while changing weights prevents a clean explanation of what caused the research order to move.

FAQ

Should every missing preference receive the worst rank?

Only under an explicitly agreed policy. This example keeps those rows pending instead of treating absence as observed weakness.

Does a first-place score justify buying the stock?

No. The score allocates research attention using chosen preferences. It is not a forecast or portfolio allocation.