Skip to content

Learn

Stock Portfolio Research

Measure stock weight drift from whole-share and lot constraints

A stock portfolio’s target weights can be impossible to implement exactly with whole shares or board lots. Rounding creates one gap; blocked orders and insufficient cash create others. Track target, executable and actual weights separately at every rebalance, retaining residual cash rather than scaling the stock portion back to one.

This guide follows three synthetic rebalances with supplied trading units and a blocked sale. It is an external implementation analysis, not verified CoTrading or broker execution. The fictional units and zero fees do not describe any actual market. Use a single currency and obtain real security-specific units before applying the method.

Verify trading-unit inputs and their effective dates

Record security identity, minimum quantity, permitted increment, odd-lot treatment and the source effective on the rebalance date. Whole-share trading and board-lot trading are not interchangeable. Corporate actions or changed units may require a separate policy for an existing odd-lot holding.

The HKEX securities-operations FAQ describes issuer-determined board lots and a separate odd-lot mechanism. A reference engine’s fixed 100-share Hong Kong simplification must not be treated as the rule for every listed stock or broker order path.

Our fixture explicitly supplies unit one for A and unit ten for B. These are invented scenario inputs, not verified Hong Kong securities. The code requires positive integer units and supported starting quantities that are nonnegative multiples of those units. It does not silently default an unknown unit to 100.

Validate the capital and target budget

Use finite positive prices, finite nonnegative cash and a positive total account value before dividing by NAV. Target weights must be finite, nonnegative and sum to the declared stock budget. The Python Decimal documentation describes the decimal and finite-number interfaces used in the sample.

Check finite values before a sum tolerance. NaN can otherwise bypass a test expressed only as abs(sum(weights)-1) > tol. The sample explicitly rejects nonfinite weights, zero NAV and unsupported identities or quantities before constructing an allocation.

This model targets full stock investment before rounding, has no borrowing and charges zero fees. Real transaction charges require reserves before determining affordable quantities. Do not reuse the zero-fee floor result and subtract a fee afterward if that would make the account negative.

Translate targets into planned lot quantities

For each stock, multiply NAV by target weight, divide by price times unit, round down to a whole number of units and multiply back. Preserve the unrounded target and the resulting planned cash. Rounding down each stock leaves cash even when the original weights sum to one.

planned_qty[s] = int((nav * target_weight[s]) // (price[s] * unit[s])) * unit[s]
planned_cash = nav - sum(planned_qty[s] * price[s] for s in basket)

The executable-weight column means feasible under this lot-rounding plan before order failures. It is not a guarantee that every planned order can fill. Actual weights are computed later from confirmed quantities and the same valuation prices, with cash still in the denominator.

Choose a deterministic allocation order if spare cash may buy additional lots. Our method does not greedily spend the remainder; it preserves the independently floored targets. A different remainder-allocation algorithm would produce another implementation policy and should be tested separately.

Work through the first two rebalances

Start with cash 1000 and no shares. Prices A 70, B 30 and target half each imply continuous quantities approximately 7.142857 and 16.666667. Unit rounding gives A seven shares and B ten, securities worth 790 and planned cash 210.

B’s buy is blocked in the first period. Only A seven fills, so actual securities are worth 490 and cash is 510. Planned B weight 0.30 must not be reported as actual; actual B weight is zero. The additional 300 cash comes from the unfilled order, not rounding.

Next prices are A 80 and B 25. Actual pretrade NAV is 1070. Targets 0.4/0.6 produce planned A five and B twenty. Selling two A shares first releases 160; B’s purchase costs 500. The actual quantities reach that plan and cash becomes 170.

PeriodNAVPlanned A/B sharesActual A/B sharesActual cash
010007 / 107 / 0510
110705 / 205 / 20170
210207 / 106 / 2080

The NAV change between rebalances comes from held securities’ price changes, not an injected contribution or a normalized target vector. The sample verifies every quantity-price-plus-cash bridge.

Trace a blocked sale into a cash-limited buy

At the third rebalance prices are A 90 and B 20. Existing A five, B twenty and cash 170 value to 1020. Target weights 0.7/0.3 imply planned A seven, B ten and planned cash 190 after flooring.

The intended B sale of ten shares is blocked. It therefore releases no 200 proceeds. A’s requested two-share purchase would cost 180, but only 170 is available, so the runner buys one share for 90. Actual holdings become A six, B twenty and cash 80.

This is a different mechanism from lot rounding. It combines an unfilled sale and a downstream funding limit. Crediting the planned sale first would create a fictional extra A share and hide the implementation gap. The event ledger records both blocked and cash_limited statuses.

Compare all three weight columns

At the final boundary targets are A 0.7 and B 0.3. Planned lot weights are approximately 0.617647 and 0.196078, plus cash 0.186275. Actual weights are approximately 0.529412 and 0.392157, plus cash 0.078431.

Each stock-plus-cash column totals one under its own account interpretation. Renormalizing actual stock weights alone would discard the cash and obscure both underimplementation and the retained B exposure. Use total NAV, not securities-only value, as the common denominator.

Keep gap sources separate: target-to-plan reflects the declared unit policy; plan-to-actual reflects execution and funding. Some real effects interact, so do not force them into a unique causal percentage if the accounting does not support it. The event chain is stronger evidence than a single aggregate drift number.

Handle infeasible or unsupported states explicitly

Zero total value cannot produce meaningful percentage weights, and missing or nonpositive prices cannot support lot calculation. Reject those inputs or return an unavailable state under a declared interface. Do not replace them with zero weights and report a successful rebalance.

An existing quantity outside the fixture’s unit contract is rejected rather than rounded away. Real odd-lot disposal needs an actual market/broker rule and perhaps another execution route. This simplified method does not verify A-share, Hong Kong or US order rules merely because the variables can store their symbols.

The sample tests NaN rejection and zero NAV, while its main path uses complete known prices and units. It does not model settlement delay, partial matching or fees. Add those limitations to a real research package before interpreting achieved weights as executable trading instructions.

Execute and accept the implementation ledger

Download the complete example. Use Python 3.12 or later and run python R635.py. Standard-library Decimal assertions verify NAVs 1000/1070/1020, cash 510/170/80, every lot multiple, the blocked-sale/cash-limit chain, and stock-plus-cash weight reconciliation.

Archive target versions, unit sources, prices, planned quantities, actual fills, residual cash and deviation reasons at every rebalance. Preserve failed orders as events instead of silently substituting targets for actual holdings. Recompute when prices, units, capital, costs or execution status change.

Keep valuation and executable-quote timestamps separate. A stale mark may still support a qualified account estimate while being unsuitable for an order calculation. If an earlier blocked order eventually fills, advance the ledger with that new event; do not rewrite the earlier period as though it had filled then, because that would change subsequent capital and quantities.

The result verifies a synthetic multi-period implementation method. It supplies no guarantee of real broker fills, no universal 100-share Hong Kong rule and no evidence that a product automatically enforces these constraints.

FAQ

Can rounded stock weights be normalized back to one?

Not to describe the actual funded account. Retain cash in the denominator and as an explicit allocation.

Can a planned sell finance another order?

Only after the sale actually executes under the account’s available-cash policy.