Skip to content

Learn

Stock Backtest Research

Diagnose A-share T-plus-1 sell blocks in a stock backtest

When an A-share backtest refuses a sale, inspect sellable inventory and the actual session before changing the signal. A correct exit condition does not make newly purchased ordinary shares immediately sellable. A blocked order must leave cash, shares and fees unchanged, and the account must carry its price risk until an eligible execution is actually supplied.

This guide isolates a single-entry ordinary A-share cash account across the 2026 National Day closure. It is not a complete per-batch settlement engine. CoTrading’s handling of T-plus-1, security-specific exceptions, settlement and live broker constraints remains planned or unverified. The code below is an independently authored diagnostic fixture with synthetic prices.

Use the rule version applicable to the trade

The SSE 2026 Trading Rules discuss selling before settlement and specified categories eligible for same-day turnover. Do not extend an exception for a different instrument to ordinary cash shares. This example deliberately selects an ordinary A-share case with a next-session sellability restriction, rather than claiming one rule covers every exchange-traded product.

The SSE publication and effective-date notice places the 2026 revision into effect on July 6, 2026, replacing the 2023 revision, and identifies implementation arrangements. For older backtests, preserve the historical version and any instrument-specific provisions. A rule read in October 2026 cannot simply be applied to every earlier trade or every board.

The fixture’s relevant state is the entry session, current session, quantity and cash. It assumes no previously held shares. That last assumption is essential: an account containing older sellable shares and a new purchase can have a different available quantity from its total position. One entry date cannot represent several acquisition batches with different sellability states.

Freeze sessions rather than adding one calendar day

The SSE 2026 National Day closure announcement specifies closure from October 1 through October 7 and reopening on October 8. The supplied session list is September 30, October 8 and October 9. It does not treat a government-adjusted working weekend as an exchange session merely because people may be working.

Buy on September 30 and the next supplied market session is October 8, not October 1. T-plus-1 in this demonstration is expressed through ordered valid sessions. A calendar-day increment would incorrectly permit an order on a closed exchange. The account checks whether the requested day belongs to the frozen session list before evaluating sellability.

For a real multi-month system, load a complete verified calendar rather than extending these three dates by weekday arithmetic. Include security suspensions and executable bars as separate inputs. An exchange may be open while one stock cannot trade; conversely, a plausible stock price in a data file does not make a closed exchange date an executable session.

Reproduce the blocked same-session sale

Start with cash 5,000 and no shares. On September 30 buy one hundred shares at 10 and charge one currency unit. Principal is 1,000, so cash becomes 3,999, quantity one hundred, cumulative fees one and realized price profit zero. The fixture records September 30 as the single entry date and 10 as its entry price.

Attempt to sell at 10.5 on the same date. The account returns T_plus_1_blocked. Cash remains 3,999, quantity one hundred, fees one and realized price profit zero. The rejection is not a completed trade and does not receive another commission. No apparent sale proceeds can be used for a different purchase while the position is still held.

If that forbidden sale were incorrectly filled, a simple round trip would appear to make forty-eight after two fees. The script calculates that number only as a counterexample. It never credits forty-eight to the account or reports it as an achievable strategy result. This distinction matters because a simulation can produce attractive returns by accepting orders that its market contract expressly forbids.

An attempted October 1 sale returns not_a_session. That is a different rejection from same-session sellability. Both leave inventory and cash intact, but the causes require different repairs: changing the session calendar cannot release same-day new shares, and changing a signal cannot open an exchange during a holiday closure.

Execute the next-session sale and close the bridge

At the October 8 opening, the supplied price is 9.5. The entry session is earlier in the ordered calendar, so the one-hundred-share sale is eligible under this fixture. It receives 950 principal less one fee, or 949 cash. Add that to the existing 3,999 and final cash is 4,948, quantity zero and total fees two.

Realized price profit is one hundred times 9.5 minus 10, or negative fifty. Net cash profit is negative fifty-two after the two commissions. The assertions require final cash minus initial capital to equal realized price profit minus cumulative fees. This reconciles trade eligibility to an actual losing account rather than stopping after a rejection message.

The decline from the rejected 10.5 opportunity to the actual 9.5 opening is a consequence of retained exposure. A blocked exit is not merely an administrative flag attached to an otherwise realized profit. The position still owns the stock, and its value can change before the next executable event. The sample supplies a later fill; a real blocked or missing opening needs another documented pending-exit policy.

Keep the single-entry model honest

An additional purchase while the account already holds shares raises additional_batch_unsupported. It does not overwrite the entry date, merge batches or pretend to maintain a per-lot freeze ledger. Overwriting could make old shares incorrectly unsellable or new shares incorrectly available, depending on which acquisition date survived. Explicit refusal is more reviewable than silently making either mistake.

The example allows a full liquidation only. Partial sales, older holdings plus same-day additions, corporate actions, transferred stock and market-specific instrument exceptions require richer inventory records. A suitable extension would keep acquisition batches, total and available quantities, allocation of each sale and settlement state. Each new rule needs independent fixtures rather than a broad claim that the small class already supports it.

This guide also does not model the complete Chinese fee schedule, stamp duties or price-limit queues. One-unit fees are declared synthetic costs. Do not compare the account’s net amount with a real broker statement without replacing that cost model and checking the relevant security, account and date. Correct T-plus-1 sequencing does not validate unrelated execution assumptions.

Run and inspect the diagnostic output

Download the complete example. Run python R597.py with Python 3.12 or newer; only the standard library is required. Inputs combine the frozen real session dates with synthetic order prices. The script neither connects to an exchange nor starts a product runner. Output includes the initial purchase, blocked account state, holiday rejection, next-session fill and final cash bridge.

The assertions check the exact rejection labels and the unchanged blocked state, as well as the completed sale record, fees, flat holdings and negative fifty-two net cash profit. When diagnosing a real result, collect the equivalent evidence: requested date, security category, effective rule version, acquired quantity by session, available inventory, rejected reason and eventual actual fill. Locate the first inconsistent state before weakening the market restriction.

Preserve the rejection as an inventory event

The blocked sale should remain in a diagnostic log even though it creates no completed order or fee. Record its requested price separately from the later executed price. This makes the apparent forty-eight-unit forbidden profit distinguishable from the actual negative fifty-two account outcome. When comparing a real engine, first check whether it kept the hundred shares through the closure; a later total can otherwise hide a premature sale followed by an equally incorrect repurchase.

FAQ

Does T-plus-1 mean the next calendar day?

Not in this fixture. Sellability advances through the supplied valid trading sessions. The National Day closure moves the next session from September 30 to October 8.

Can this class manage mixed old and new share batches?

No. It explicitly rejects additional batches. A full inventory system must represent batch-specific availability instead of relying on one entry date.