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:
- Entry orders can be ignored. An entry method may be ignored when a position is open and an exit order or a
Set-method order is working that would be used to open a position in the opposite direction. In plain terms: a working stop or target plus a reversal entry can cause the entry to be silently dropped. - Entries are capped by
EntriesPerDirection. With the default of 1 andEntryHandling.AllEntries, a secondEnterLong()call does nothing while already long — even with a different signal name. - Unfilled orders expire at the end of the bar. By default, managed limit and stop entries are cancelled if they are not resubmitted on the next bar update. You must call the method again each bar, or set
IsLiveUntilCancelledusing the advanced overloads. - Exit methods need a matching position.
ExitLong()with afromEntrySignalthat does not match an open entry does nothing.
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:
SubmitOrderUnmanaged(barsInProgress, orderAction, orderType, quantity, limitPrice, stopPrice, oco, signalName)ChangeOrder(order, quantity, limitPrice, stopPrice)CancelOrder(order)
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:
- Capture order references in
OnOrderUpdate(). Do not rely on the return value ofSubmitOrderUnmanaged()alone; assign yourOrdervariables when the update arrives, matching onorder.Name. - 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:
- Partial fills. Size protective orders from the filled quantity and adjust them as more fills arrive.
- Rejections. With
RealtimeErrorHandling.IgnoreAllErrors, a rejected stop does not stop the strategy. Your code must detect the rejection inOnOrderUpdate()and either resubmit or flatten. An unprotected position is the worst failure mode in automated trading. - Overfills. If a target fills while the OCO-linked stop is also executing, you can end up with an unintended opposite position. Decide how your strategy detects and resolves this; the
IgnoreOverfillproperty changes how NinjaTrader reacts. - Historical-to-realtime transition. Orders created during historical processing must be converted with
GetRealtimeOrder()when the strategy transitions to realtime, or your references point to stale objects. - Connection loss. Decide what the strategy does on reconnect — sync to the account position, or stop.
When to choose managed
- Single-entry strategies with a fixed stop and target.
- Strategies built or prototyped in Strategy Builder. Our Strategy Builder vs custom code comparison covers that trade-off.
- Anything you want to backtest quickly without writing order-state plumbing.
- Developers still learning the event model. The rules protect you while you learn.
When to choose unmanaged
- Simultaneous long and short working orders — for example, a breakout strategy that places a buy stop above and a sell stop below a range. The managed rules can block this pattern.
- Custom bracket logic — scaling out at multiple targets with independently moving stops.
- Grid and scale-in strategies that stack multiple working orders.
- Execution algorithms — chasing a limit order, iceberg-style slicing, or time-based cancel-and-replace. See our order execution optimization guide.
- Multi-instrument strategies where order state on one series drives decisions on another.
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:
- 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.
- Market Replay. Replay volatile sessions — CPI mornings, FOMC afternoons — and watch how brackets behave when price gaps.
- Sim account in realtime for at least a week, deliberately disconnecting and reconnecting to test recovery logic.
- 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:
- Does the strategy ever need working orders on both sides of the market at once? If yes, go unmanaged.
- Does it scale out at more than one target with stops that move independently? If yes, unmanaged or managed with advanced order handling.
- Does it need to modify orders between bars, on tick or market-depth events? Managed advanced handling may be enough; test it first.
- Will anyone other than you maintain this code? If yes, lean managed. Unmanaged state machines are harder to hand over.
- 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:
- One small state enum — Flat, EntryWorking, InPosition, Exiting — and transitions only in
OnOrderUpdate()andOnExecutionUpdate(). - Named helper methods such as
SubmitBracket(int qty, double fill)andFlattenAll(string reason)instead of inline order calls scattered through the file. - Consistent signal names so every order in the log maps cleanly back to the code that sent it.
Common mistakes
- Mixing approaches. Calling a managed method in an unmanaged strategy only errors — it will not submit.
- Setting order references to null too early. Wait for a terminal state (Filled, Cancelled, Rejected) before clearing a reference.
- Submitting brackets from
OnBarUpdate(). On fast fills you may submit before the entry is confirmed, or twice. UseOnExecutionUpdate(). - Assuming backtest behavior equals live behavior. Queue position, partial fills, and rejections barely exist in a simple backtest.
- No kill switch. Every unmanaged strategy needs a path that cancels all working orders and flattens.
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.