For example a long limit entry order of 10 contracts, may be completely filled by the following 3 OnExecution events, first a PartFilled event with say 5 contracts, a second PartFilled event with 4 contracts and finally reaching a Filled state with the last contract.
Given that my order life cycle understating is correct, my question, in the case of an unmanaged approach, with the above example, will 3 pairs of stops and targets be issued or one pair?
If 3 sets of stops and targets are issued, would the code below need to be enhanced to track each order object separately, perhaps in a list, such that they could be cancelled or updated or set to null elsewhere. In my example, would only the 3rd issued order object for the last target and stops be available? Given that the same variables are used for storing the object instance.
If only one pair of stop/targets are ever issued, then Ninja's unmanaged order is actually doing lots of behind the scenes work and updating the first pair of stops and targets. If so the code will work just fine.
protected override void OnExecution(IExecution execution) { /* We advise monitoring OnExecution to trigger submission of stop/target orders instead of OnOrderUpdate() since OnExecution() is called after OnOrderUpdate() which ensures your strategy has received the execution which is used for internal signal tracking. */ if (longEntry != null && longEntry == execution.Order) { if (execution.Order.OrderState == OrderState.Filled // long entry is filled -yeah! || execution.Order.OrderState == OrderState.PartFilled // entry is partially filled - how to handle partial fill??????? || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0)) // long entry was cancelled before any entries { //OCO stops and targets stopLossLong = SubmitOrder(0, OrderAction.Sell, OrderType.Stop, execution.Order.Filled, 0, execution.Order.AvgFillPrice - 4 * TickSize, "LongExits", "StopLossLong"); targetLong = SubmitOrder(0, OrderAction.Sell, OrderType.Limit, execution.Order.Filled, execution.Order.AvgFillPrice + 8 * TickSize, 0, "LongExits", "TargetLong"); // Resets the longEntry object to null after the order has been filled if (execution.Order.OrderState != OrderState.PartFilled) { longEntry = null; } } } if (shortEntry != null && shortEntry == execution.Order) { if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0)) { stopLossShort = SubmitOrder(0, OrderAction.BuyToCover, OrderType.Stop, execution.Order.Filled, 0, execution.Order.AvgFillPrice + 4 * TickSize, "ShortExits", "StopLossShort"); targetShort = SubmitOrder(0, OrderAction.BuyToCover, OrderType.Limit, execution.Order.Filled, execution.Order.AvgFillPrice - 8 * TickSize, 0, "ShortExits", "TargetShort"); // Resets the shortEntry object to null after the order has been filled if (execution.Order.OrderState != OrderState.PartFilled) { shortEntry = null; } } } // Reset our stop order and target orders' IOrder objects after our position is closed. if ((stopLossLong != null && stopLossLong == execution.Order) || (targetLong != null && targetLong == execution.Order)) { if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled) { stopLossLong = null; targetLong = null; } } if ((stopLossShort != null && stopLossShort == execution.Order) || (targetShort != null && targetShort == execution.Order)) { if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled) { stopLossShort = null; targetShort = null; } } }
Comment