Managed orders
Managed orders
Position-aware intent: say "be long 5" or "go flat" and let the framework work out the reversal arithmetic and guard against double entries.
Managed orders (position-aware intent)
Managed orders are the simplest way to trade and the right default for most strategies. You state the
position you want and the framework works out the market order. EnterLong and
EnterShort handle reversals: from a short position, EnterLong buys back the short
and opens the long in one order. ExitLong and ExitShort return to flat and do
nothing when you are not on that side. Quantities are in trading units (lots on a base-unit forex venue);
see Instrument metadata & sizing.
C#EnterLong(5); // flat: buy 5; short 3: buy 8 (cover 3 + open 5); already long: nothing
EnterShort(5); // mirror of EnterLong
ExitLong(); // long: sell to flat; otherwise nothing
ExitShort(); // short: buy to flat; otherwise nothing
if (!HasPendingManagedOrder && signal) EnterLong(qty); // wait while one is in flight
| Call | Behaviour by starting position |
|---|---|
EnterLong(qty) |
Flat: enters long. Short: reverses to long. Already long: nothing (one entry per direction). |
EnterShort(qty) |
Mirror of EnterLong. |
ExitLong() |
Long: closes with a market order. Flat or short: nothing. |
ExitShort() |
Short: closes with a market order. Flat or long: nothing. |
EnterLong and EnterShort throw ArgumentOutOfRangeException for a
quantity of zero or less; close with ExitLong / ExitShort instead. While the host
has paused a live run for feed recovery, the two entry calls are held and report the reason through
NotifyEntryBlocked; the exit calls still go out.
The pending-order guard
The managed helpers keep at most one order in flight. While it is live (submitted and not yet terminal),
HasPendingManagedOrder is true and every managed call does nothing, so a strategy that
fires on each bar cannot stack duplicate entries before the first one fills. Two EnterLong
calls in the same OnBar therefore send one order. The flag clears when the managed order
is cancelled, rejected or expired, and for a filled order when the position shows the fill or the
venue's settle time has passed. The base
class feeds each order update to the managed layer before your OnOrderUpdate runs.
C#protected override void OnBar()
{
if (HasPendingManagedOrder) return; // let the previous intent settle
if (crossUp && IsFlat) EnterLong(_qty.Value);
if (crossDown && IsLong) ExitLong();
}
A fill counts as settled once the position shows it. Several venues state the position after the order, so
the base class also feeds each position statement to the managed layer before your
OnPositionUpdate runs, and a managed order that filled stays in flight until the position
shows the fill.
A managed order can also fail before it becomes an order: the placement call faults or is cancelled, for
example while the connection is down. No OnOrderUpdate ever comes for it, so the base class
calls OnManagedOrderFailed instead, once per such order, on the strategy thread and never
inside the callback that placed it. HasPendingManagedOrder is already false at that point.
Override it to put the strategy back in a known state. An order the venue rejects is not reported here:
it reaches OnOrderUpdate as a rejected order, so react to one or the other, never
to both.
C#protected override void OnManagedOrderFailed(string clientTag, Order? order, Exception? error)
{
// order is always null: no order exists for a placement that failed.
Log($"Managed order {clientTag} was not placed: {error?.Message}");
_exitRequested = false; // back to a known state
}
OnManagedOrderFailed faults the run, like a throw from any other callback.EnterLong states the intent and
the ATM protects the result.Behind the surface
The helpers delegate to an IManagedOrderController that the base class creates on first use
through IStrategyContext.CreateManagedOrderController. Its members are the four intent calls,
HasPendingOrder, and HandleOrderUpdate(Order), which the base class calls with
every order update so the controller can clear its pending flag. The base class also calls
HandlePositionUpdate(Position) with every position statement of the run's instrument, and
subscribes to the controller's Failed event, which it forwards to
OnManagedOrderFailed. The controller recognises its own orders by
the ClientTag it stamps on them. Strategy code uses the protected helpers; a custom host
supplies its own controller through the context factory without any change to the strategy.