Raw orders & brackets

Strategies

Raw orders & brackets

When managed intent is not enough: submit order requests directly, attach a server-side protective bracket, and modify or cancel by id.

Raw orders & brackets

For full control over price, type and time-in-force, submit an OrderRequest directly with PlaceOrder. Unlike managed intent, a raw order does not reason about your position — it does exactly what you ask — so you own the lifecycle. Results arrive asynchronously through OnOrderUpdate and OnFill, which is where you react to fills, rejections and cancels. The request's Instrument must equal the run's Instrument — a strategy trades only the instrument it runs on.

To get a protective stop and target without managing the OCO yourself, attach a BracketSpec to the entry. The provider places the bracket server-side and cancels the remaining leg when one fills. Bracket prices are absolute (not offsets) — the strategy knows the chart, and absolute prices keep the spec venue-neutral.

C#using TradeStrike.Pipeline.Trading;

// Market entry with a server-side bracket (absolute prices).
PlaceOrder(new OrderRequest(
    Instrument: Instrument,
    Side: OrderSide.Buy,
    Type: OrderType.Market,
    Quantity: 5m,
    Bracket: new BracketSpec(StopLossPrice: 4340m, TakeProfitPrice: 4360m)));

// Resting limit order:
PlaceOrder(new OrderRequest(Instrument, OrderSide.Buy, OrderType.Limit, 2m, LimitPrice: 4350.25m));

ModifyOrder(orderId, new OrderModification(NewLimitPrice: 4351m));
CancelOrder(orderId);
Flatten();   // close this run's open position now

The constructor draws on a small set of enums. A BracketSpec needs at least one of its two prices, and for a buy the stop must be below the target (flipped for a sell) — validated at construction.

Type Values
OrderSide Buy, Sell
OrderType Market, Limit, Stop, StopLimit
TimeInForce Day, Gtc, Ioc, Fok
OrderState PendingWorkingPartiallyFilledFilled (or Cancelled / Rejected / Expired / Stale)

Limit requires a positive LimitPrice, Stop a positive StopPrice, StopLimit both, and Market neither — Validate() enforces this. Stale is a soft-terminal a provider re-evaluates on reconnect; the other terminals are final.

The full OrderRequest

The examples above use the common arguments, but OrderRequest carries a few more for cases that need them. The complete shape is:

OrderRequest.csnew OrderRequest(
    Instrument, Side, Type, Quantity,
    LimitPrice: null, StopPrice: null,
    TimeInForce: TimeInForce.Day,
    Bracket: null,            // BracketSpec for a server-side stop/target
    OcoGroupId: null,         // link orders into one one-cancels-other group
    ClientTag: null,          // your own label, echoed back on the resulting Order
    InstrumentKey: null,      // cross-venue routing: the canonical instrument id…
    InstrumentVenue: null,    // …and the venue the symbol is native to (data feed A, broker B)
    OriginId: null);          // owner provenance — stamps which component created the order
  • OcoGroupId ties several working orders into one one-cancels-other group (a manual OCO, where BracketSpec is the automatic two-leg case).
  • ClientTag is a free label echoed back on the resulting Order to correlate it with your own bookkeeping.
  • InstrumentKey / InstrumentVenue drive cross-venue routing — sourcing data from one venue while routing the order to another; the execution layer localises the wire symbol. null means same-venue (no rewrite).
  • OriginId records which component created the order, so an auto-management engine (a chart-trader ATM, the copier) only acts on positions it opened.

Call request.Validate() to fail fast on an inconsistent request; the same validation runs on submit. Modifications travel as an OrderModification delta — null fields mean "leave unchanged".

Reading orders & fills

OnOrderUpdate(Order) hands you the live order snapshot. Beyond State it carries FilledQuantity, AverageFillPrice, RemainingQuantity, BrokerOrderId, RejectReason, the originally-submitted OriginalRequest, and IsTerminal (true once it can no longer change). Orders are keyed by OrderId (a GUID wrapper); keep the id to modify or cancel later. The run's own live orders are also available as the WorkingOrders list.

OnFill(Fill) carries each raw execution — Quantity, Price, Commission, TimestampUtc — while OnOrderUpdate fires alongside with the rolled-up order state. Use OnFill for per-execution bookkeeping (commission tracking, fill logs) and OnOrderUpdate for state transitions.

C#protected override void OnOrderUpdate(Order order)
{
    if (order.State == OrderState.Rejected)
        Log($"rejected: {order.RejectReason}");
    else if (order.IsTerminal && order.State == OrderState.Filled)
        Log($"filled {order.FilledQuantity} @ {order.AverageFillPrice}");
}

protected override void OnFill(Fill fill)
    => Log($"exec {fill.Quantity} @ {fill.Price} (comm {fill.Commission})");

Reusable bracket presets

For named SL/TP templates independent of a running strategy, the TradeStrike.Pipeline.Trading.Brackets namespace offers BracketPreset (a name plus stop / target offsets in Price or Ticks, as positive magnitudes) and the pure helper BracketPresetMath.ComputeBracketPrices(entryPrice, side, preset, tickSize), which resolves the magnitudes into the absolute SL/TP prices a BracketSpec wants. Long entries put the stop below and target above; shorts flip. See the Trading reference.