Trading bot scope builder

Engineering note · Paired-order lifecycle

A Paired Buy and Sell EA Needs One Explicit Cycle State

Define exactly what happens when one leg stops, the survivor closes and the EA restarts so one paired-order cycle cannot create duplicate replacements.

8 minute read

Buyer question

What should the EA do after one side of a paired buy and sell cycle closes?

Failure pattern

A tick sees no complete pair and creates a replacement while the survivor still belongs to the old cycle.

Acceptance rule

The survivor remains untouched, then its closure opens the next pair exactly once.

The entry rule is only the first transition

A paired-order EA sounds simple: open one buy and one sell, let one leg stop, keep the other leg alive and start a new pair after the survivor closes. The difficult part is not creating two orders. The difficult part is proving which cycle owns every order when ticks, trade events and restarts arrive in an inconvenient order.

A common implementation checks the account on every tick. If it cannot find both legs, it assumes the pair is missing and opens another one. That shortcut breaks the stated lifecycle. After the first stop, the account should contain one survivor. The missing leg is expected, not an error. Replacing it changes exposure and creates a different strategy.

The safer design gives each pair a stable cycle identity and stores the ticket or order identity of both legs. The EA then advances through explicit states. It never infers a new cycle from position count alone. Position count is evidence, but the recorded cycle state decides what the evidence means.

Use a cycle state, not a pair-present boolean

The state must answer three questions at all times: which tickets belong to this cycle, whether a survivor is expected and whether the one-time next-cycle transition has already started. That information must survive a terminal restart.

StateWhat is allowed
IDLENo cycle is owned. A new pair may be created once.
PAIR_SUBMITTEDBoth order identities are recorded before another entry decision is allowed.
PAIR_OPENThe buy and sell legs belong to the same cycle and both are active.
SURVIVOR_OPENOne leg closed. The remaining leg stays open and no replacement is permitted.
CYCLE_CLOSINGThe survivor has closed, but the next pair has not yet been created.
NEXT_PAIR_SUBMITTEDThe one-time transition owns the next pair until both returned identities are stored.

The smallest complete implementation

  1. 1

    Create one cycle ID.

    Generate it before submitting either leg and attach it through magic number, comment and persisted local state where the platform permits.

  2. 2

    Record both returned identities.

    Do not report PAIR_OPEN until the buy and sell submissions have known outcomes. A rejected second leg needs an explicit recovery state.

  3. 3

    Process closures once.

    Trade-history evidence advances one known ticket from open to closed. Re-reading the same closure must be harmless.

  4. 4

    Protect the survivor state.

    When one ticket closes, store SURVIVOR_OPEN with the remaining ticket. Entry logic stays locked.

  5. 5

    Own the next-cycle transition.

    After the survivor closes, move to CYCLE_CLOSING before any new submission. Only that state may create the next pair.

  6. 6

    Rebuild before trading.

    On initialization, reconcile persisted tickets with open trades and history before enabling any new order path.

Three acceptance tests to freeze before coding

These tests define completion in observable terms. They also close the most expensive revision loop: arguing after delivery about whether a stopped leg should be replaced.

Test 1

One leg stops

Input: The buy leg hits its stop while the sell leg remains open.

Pass: The buy leg is marked closed, the sell leg remains the only survivor and no replacement buy order is submitted.

Test 2

The survivor closes

Input: The remaining sell leg reaches its own exit after the first leg is already closed.

Pass: The old cycle closes and exactly one new buy and sell pair is created. Repeated ticks cannot create a second pair.

Test 3

Restart between closures

Input: MT4 or the EA restarts while one leg is closed and the survivor is still open.

Pass: The EA reconstructs the same cycle from persistent identities, keeps the survivor and does not duplicate either leg.

Scope boundaries that should be written down

The lifecycle does not decide the trading signal, lot size, stop distance or profit target. Those are separate rules. It also does not decide whether both initial submissions must succeed atomically. MT4 cannot make two independent broker requests atomic, so the specification must say what happens when one initial order is accepted and the other is rejected.

Source ownership, symbol selection, hedging permission, broker stop levels, magic-number isolation and manual order handling also need explicit boundaries. A paired-order state machine can be correct while the broker rejects hedged exposure or another EA uses the same ownership key. Keep those failures visible instead of turning them into silent retries.

Finally, passing these tests proves lifecycle behavior. It does not prove profit, drawdown, fill quality or future trading results. Those claims require separate evidence and should never be hidden inside software acceptance.

Free next step

Write the three transitions before describing the indicators

Use the free scope builder to record the stopped-leg rule, the survivor rule and the restart rule. A developer can then quote one deterministic lifecycle instead of guessing what a complete cycle means.

Trading risk disclosure

Futures and forex trading contains substantial risk and is not for every investor. An investor could potentially lose all or more than the initial investment. Risk capital is money that can be lost without jeopardizing one's financial security or lifestyle. Only risk capital should be used for trading, and only those with sufficient risk capital should consider trading. Past performance is not necessarily indicative of future results.

Hypothetical performance disclosure

Hypothetical performance results have many inherent limitations. No representation is being made that any account will or is likely to achieve profits or losses similar to those shown. There are frequently sharp differences between hypothetical performance results and the actual results subsequently achieved by any particular trading program. Hypothetical results are generally prepared with the benefit of hindsight, do not involve financial risk, and cannot completely account for the impact of financial risk in actual trading. Market conditions, the ability to withstand losses, and adherence to a trading program can all materially affect actual results.

NinjaTrader trademark disclosure

NinjaTrader® is a registered trademark of NinjaTrader Group, LLC. No NinjaTrader company has any affiliation with the owner, developer, or provider of the products or services described herein, or any interest, ownership or otherwise, in any such product or service, or endorses, recommends or approves any such product or service.

Testimonials, when shown, may not represent the experience of other clients and are not a guarantee of future performance or success. Virtual-currency trading carries additional risks. See the CFTC customer advisories. Read the full financial risk disclaimer.