Skip to content

Learn

Stock Strategy Development

Turn a stock strategy idea into a backtestable specification

To turn a stock idea into a backtestable strategy, write a deterministic specification before asking for implementation. “Buy strong stocks and leave when momentum weakens” does not define the input window, decision time, holding state or precedence of conflicting rules. A useful specification lets a second implementer produce the same signals from the same small dataset.

The deliverable here is a strategy contract with a normal path, a conflict path and a later execution schedule. The miniature Python implementation is a manually confirmed example, not a general natural-language parser or a tested AI generation service. CoTrading natural-language strategy capabilities remain planned or unverified. All data and instrument names below are invented.

Replace every ambiguous phrase with a field

Choose one market first. This example uses a single fictional US stock, US:ALFA, in a USD cash account. The input consists of ordered daily completed closes and separate next-session opens. The universe is fixed before evaluation. A production universe also needs effective membership dates, delisting treatment and a rule for securities that change identifiers.

Translate “strong” into a numeric comparison: the completed close must be strictly greater than the arithmetic average of the latest three completed closes, including the current one. Three observations are required. With fewer observations the target is zero. Equality also produces zero. This short window is selected for hand calculation, not as an investment parameter recommendation.

Translate “hold” into a daily target state rather than a one-day crossing event. A qualifying day targets the long state again even if yesterday already qualified. Translate “leave” into a zero target whenever the comparison is false. The example permits only zero or one, with one meaning eligible for the separately defined long allocation; it is not a signed buy-or-sell order.

Write the timing contract separately

The score is available after the completed session close. It can first affect the next supplied tradable open. Save decision index, data cutoff, intended execution index and actual execution status as distinct fields. A target of one does not prove that shares were purchased, because a missing open, insufficient cash or an execution restriction may prevent the trade.

Investor.gov’s order reference explains why an observed last price is not a guaranteed execution price. Here next-open execution is a simulation convention with explicitly supplied prices. It is not a promise that a real market order will fill at that exact value. Do not infer an exchange or broker integration from this convention.

For a full account implementation, specify the long allocation, integer-share rounding, a cash reserve, commissions and price slippage. State whether the system rebalances daily or only when entering and leaving the long state. This article validates signals and their schedule, so it does not claim that a complete cost-accounting engine has been implemented by the small function.

Define conflict precedence before writing code

Introduce a risk block as an explicit Boolean input. If a completed day’s block is true, the target must be zero even when the price comparison is true. The precedence order is therefore insufficient history, then risk block, then the numeric comparison. A reviewer should not have to guess whether “buy strength” overrides “do not hold during a risk block.”

Specify whether a block cancels only a new entry or also requests exit from an existing position. This fixture chooses the latter: it sets the desired state to zero. The subsequent execution layer must still establish whether an exit can fill. A block is a policy input in this example, not an inferred news classification or a model-generated risk judgment.

Keep unavailable risk information different from false. The fixture supplies a complete block set; a real contract should reject unknown status or choose a conservative documented default. Otherwise missing risk data silently authorizes positions. That error can survive otherwise correct moving-average arithmetic and must have its own input validation.

Work through the ordinary path

Use closes of 10, 10, 10, 12, 13, 9 and 8. Indices start at zero. The first two indices have insufficient history. At index two, the close equals its three-observation average, so the strict comparison remains false. Index three compares twelve with the average of ten, ten and twelve, producing one. Index four also qualifies.

At index five, nine is below the average of twelve, thirteen and nine, so the target returns to zero. Index six remains zero. The completed-close signal sequence is therefore [0, 0, 0, 1, 1, 0, 0]. This sequence contains a sustained holding state, not two independent instructions to buy a full position.

Apply exactly one execution delay to obtain next-open targets: [0, 0, 0, 0, 1, 1, 0] within the supplied seven rows. The final close would generate a target for a later open that is not present in the fixture. Do not fabricate that missing session or count its target as an executed transaction.

Make the conflict path independently checkable

Set the risk block at index four. The price still qualifies, but precedence changes that day’s close target to zero. The conflict sequence becomes [0, 0, 0, 1, 0, 0, 0]. This exercises a different rule from the equality boundary and demonstrates why a prose instruction needs an ordered decision table.

The standalone file docs/growth/research/round-5/examples/R572.py implements the confirmed specification with the standard library. Its essential decision is:

if i < 2 or i in risk_blocks:
    target = 0
else:
    average = sum(closes[i - 2:i + 1]) / 3
    target = int(close > average)

Run python docs/growth/research/round-5/examples/R572.py. Assertions check the normal sequence, the risk conflict, the delayed targets and an all-equal three-row boundary. These are executable acceptance examples. They do not establish that arbitrary wording can be translated reliably, or that an AI-produced implementation matches other untested instructions.

Hand the specification to another implementer

Package the universe definition, input schema, window, comparison operator, warmup rule, conflict precedence and execution convention together. Add the exact fixture and expected outputs. Require the new implementation to explain any difference by row and stage before running a larger backtest. A differently named class is acceptable; a different equality rule is a strategy change.

When a requirement changes, update the prose contract, acceptance sequence and implementation in the same version. Record which change altered signals and which altered only quantities or costs. Review unclear phrases such as “recent,” “liquid,” “small position” and “quickly” until they become measurable fields or explicit dependencies. This provides a concrete stopping condition: the specification is ready when independent code reproduces both paths without asking the author to interpret the original sentence again.

Download the complete example. Save it as R572.py and run python R572.py with Python 3.12 or later. It uses only the standard library and synthetic data.

Make implementation review independent of the original prompt

Give a reviewer the contract and fixture without the original conversational wording. Ask them to derive the target at the first complete window, the first qualifying row, the conflict row and the first exit. If they need the author to explain what “strong” meant, the specification still contains an unresolved dependency. This exercise tests whether the handoff artifact is sufficient, rather than whether two people remember the same discussion.

Keep a change log that identifies the affected layer. Altering strict greater-than to greater-than-or-equal changes decision logic. Adding a commission changes account arithmetic. Moving execution to a later open changes scheduling. Those changes need different acceptance outputs, even when they originate in a single request such as “make it more conservative.” Require the implementation to reject unsupported target states and missing mandatory inputs. A smaller, explicit specification that refuses an undefined case is easier to verify than a broader function that fills gaps with guesses and returns plausible-looking signals.

Attach the specification version to each signal artifact. Review that identity before comparing inputs, sequences and execution differences, and carry real-data coverage as a separate outstanding dependency.

FAQ

Is this an automatic strategy generator?

No. It is one manually specified rule and a deterministic implementation used for acceptance testing.

Why separate targets from orders?

A desired holding state may repeat or fail to execute. Mixing those states can create duplicate purchases or imaginary fills.