NinjaScript Managed vs Unmanaged Orders: A Developer Guide

Category: Strategy Guides

When to use NinjaScript's managed vs unmanaged order approach: internal order handling rules, SubmitOrderUnmanaged, OCO, and OnOrderUpdate patterns.

Every NinjaScript strategy makes one architectural decision before it places a single order: managed or unmanaged. Choose managed, and NinjaTrader tracks signals, links stops and targets, and quietly blocks orders it thinks are dangerous. Choose unmanaged, and you get raw access to submit, change, and cancel anything — along with full responsibility for every edge case.

Most strategies that misbehave live do so because the developer did not understand which rules applied. This guide explains both approaches, the internal order handling rules that trip people up, and working code patterns for each. If you are new to strategy development, start with our guide to building custom NinjaTrader strategies with NinjaScript.

The managed approach in one paragraph

Managed order methods are the ones most people learn first: EnterLong(), EnterShort(), ExitLong(), EnterLongLimit(), SetStopLoss(), SetProfitTarget(), SetTrailStop(). You express intent — "be long one contract with a 20-tick stop" — and NinjaTrader handles the mechanics. Stops and targets submitted through Set methods are automatically tied to the entry signal, OCO-linked, and cancelled when the position closes. Reversals are handled for you: calling EnterShort() while long closes the long and opens the short.

protected override void OnStateChange()
{
    if (State == State.SetDefaults)
    {
        Name = "ManagedExample";
        EntriesPerDirection = 1;
        EntryHandling = EntryHandling.AllEntries;
    }
    else if (State == State.Configure)
    {
        SetStopLoss("Long", CalculationMode.Ticks, 20, false);
        SetProfitTarget("Long", CalculationMode.Ticks, 40);
    }
}

protected override void OnBarUpdate()
{
    if (CurrentBar < 20) return;
    if (CrossAbove(Close, SMA(20), 1))
        EnterLong(1, "Long");
}

That is a complete bracketed strategy in a dozen lines. The convenience is real — and so is the hidden logic behind it.

The internal order handling rules

To prevent overfills and accidental double positions, the managed approach enforces a set of internal order handling rules. NinjaTrader's documentation spells them out; the ones that surprise developers most often are these:

When an order is ignored, NinjaTrader writes a message to the Log tab. Check it before assuming your logic is wrong. The rules are there for good reasons — they stop many beginner strategies from doubling exposure — but they are invisible until they bite.

The unmanaged approach

Set IsUnmanaged = true in OnStateChange() and NinjaTrader turns off signal tracking and the internal order handling rules. You get three primary methods:

The two approaches cannot be mixed. Once IsUnmanaged is true, calling EnterLong() or SetStopLoss() generates an error in the log. There are no automatic brackets, no automatic reversals, and nothing stops you from submitting a buy while a sell is working. The only rules left are the ones imposed by your broker and the exchange.

private Order entryOrder, stopOrder, targetOrder;

protected override void OnStateChange()
{
    if (State == State.SetDefaults)
    {
        Name = "UnmanagedExample";
        IsUnmanaged = true;
        RealtimeErrorHandling = RealtimeErrorHandling.IgnoreAllErrors;
    }
}

protected override void OnBarUpdate()
{
    if (CurrentBar < 20) return;
    if (entryOrder == null && Position.MarketPosition == MarketPosition.Flat
        && CrossAbove(Close, SMA(20), 1))
    {
        SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, 1, 0, 0, "", "Entry");
    }
}

OnOrderUpdate and OnExecutionUpdate: where unmanaged logic lives

In unmanaged code, OnBarUpdate() decides when to trade, but OnOrderUpdate() and OnExecutionUpdate() decide what happens next. Two principles:

  1. Capture order references in OnOrderUpdate(). Do not rely on the return value of SubmitOrderUnmanaged() alone; assign your Order variables when the update arrives, matching on order.Name.
  2. Submit protective orders from OnExecutionUpdate(). Brackets should be placed against the actual filled quantity and price, including partial fills.
protected override void OnOrderUpdate(Order order, double limitPrice, double stopPrice,
    int quantity, int filled, double averageFillPrice, OrderState orderState,
    DateTime time, ErrorCode error, string comment)
{
    if (order.Name == "Entry")  entryOrder  = order;
    if (order.Name == "Stop")   stopOrder   = order;
    if (order.Name == "Target") targetOrder = order;

    if (order == entryOrder && (orderState == OrderState.Cancelled || orderState == OrderState.Rejected))
        entryOrder = null;
}

protected override void OnExecutionUpdate(Execution execution, string executionId,
    double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
{
    if (entryOrder != null && execution.Order == entryOrder
        && execution.Order.OrderState == OrderState.Filled)
    {
        string oco = "OCO-" + Guid.NewGuid().ToString("N");
        double fill = execution.Order.AverageFillPrice;
        SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.StopMarket, execution.Order.Filled,
            0, fill - 20 * TickSize, oco, "Stop");
        SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit, execution.Order.Filled,
            fill + 40 * TickSize, 0, oco, "Target");
        entryOrder = null;
    }
}

Note the unique OCO string, built from a GUID rather than the clock (in a fast backtest, clock-based IDs can repeat). An OCO ID that has already been used cannot be reused; resubmitting with the same ID can get the order rejected. Generating a fresh ID per bracket avoids that class of bug entirely.

What you take on with unmanaged code

Turning off the safety rules means your code now owns every scenario the rules used to cover. At minimum, handle:

When to choose managed

When to choose unmanaged

A middle path: managed with advanced order handling

Before jumping to unmanaged, consider the managed approach's advanced overloads. They let you submit, change, and cancel managed orders from any event-driven method, keep orders live across bars with isLiveUntilCancelled, and still retain signal tracking. NinjaTrader flags this area as intended for experienced programmers. For many strategies — trailing a stop from OnMarketData(), or keeping a resting limit entry alive — it provides the control you need without surrendering the internal rules.

Testing order logic before it goes live

Order-handling bugs rarely show up in a bar-based backtest, because historical fills are simplified. Test in stages:

  1. Backtest with tick replay or a high-resolution fill series so intrabar order interaction is modeled more closely. Our Strategy Analyzer guide covers the settings.
  2. Market Replay. Replay volatile sessions — CPI mornings, FOMC afternoons — and watch how brackets behave when price gaps.
  3. Sim account in realtime for at least a week, deliberately disconnecting and reconnecting to test recovery logic.
  4. Live with one micro contract before scaling.

Log every order state change with Print() during testing. When something goes wrong at 2 a.m., a complete order log turns a mystery into a five-minute fix.

A decision checklist

If you are unsure which approach a new strategy needs, answer these five questions in order:

  1. Does the strategy ever need working orders on both sides of the market at once? If yes, go unmanaged.
  2. Does it scale out at more than one target with stops that move independently? If yes, unmanaged or managed with advanced order handling.
  3. Does it need to modify orders between bars, on tick or market-depth events? Managed advanced handling may be enough; test it first.
  4. Will anyone other than you maintain this code? If yes, lean managed. Unmanaged state machines are harder to hand over.
  5. Is this a prototype? Build it managed. Port to unmanaged only once the edge is confirmed in testing and the managed rules are the actual constraint.

A common and sensible workflow: prototype managed, validate the idea, then rewrite the order layer unmanaged only if the rules get in the way. Rewriting order handling is far cheaper than debugging an unmanaged state machine for an idea that never had an edge.

Structuring unmanaged code so it stays readable

Unmanaged strategies grow messy fast. Three habits keep them maintainable:

Common mistakes

Our list of automated trading mistakes to avoid covers more failure modes beyond order handling.

Skip the plumbing

Robust order handling is the least glamorous and most important part of an automated strategy. NocNoe's Pro algos ship with order management already hardened — brackets from fills, rejection handling, daily loss caps, and flatten-on-disconnect — so you can focus on the trading logic, not the edge cases. See NocNoe pricing to learn more.

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.

Hypothetical performance results have many inherent limitations, some of which are described below. No representation is being made that any account will or is likely to achieve profits or losses similar to those shown; in fact, there are frequently sharp differences between hypothetical performance results and the actual results subsequently achieved by any particular trading program. One of the limitations of hypothetical performance results is that they are generally prepared with the benefit of hindsight. In addition, hypothetical trading does not involve financial risk, and no hypothetical trading record can completely account for the impact of financial risk of actual trading. For example, the ability to withstand losses or to adhere to a particular trading program in spite of trading losses are material points which can also adversely affect actual trading results. There are numerous other factors related to the markets in general or to the implementation of any specific trading program which cannot be fully accounted for in the preparation of hypothetical performance results and all which can adversely affect trading results.

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 ones' financial security or life style. 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.