I have a strategy that I am backtesting in Market Replay with tick data. The entries fill just fine and sometimes take partial fills at different prices due to bid-ask variability with market orders. The entry is given a unique name; however, when that name is referenced in the take profit logic, the ninjatrader engine thinks the first partial fill is the entire order and will cancel the remaining exit orders once the first order is filled. I will provide an example.
A signal is created to short at market for $MNQ. The algorithm places a short order for 4 contracts. 3 contracts are filled at 22,061.50 and 1 contract is filled at 22,061.25. Both fills are given the same name ShortSFP_1305. There are 4 take profit levels arbitrarily set to values below entry price (for short position). Once the first 3 take profit levels are executed, the last take profit order is automatically canceled. I believe this is because the order engine is processing the 3 contracts filled at 22,061.50 as the entire position. I think it's doing this because both fill values have the name "ShortSFP_1305". I don't know how to fix this problem. If anybody is able to provide a clear answer, I would be very appreciative.
//Short entry logic if (downSignal && Position.MarketPosition != MarketPosition.Long)
{[INDENT]string shortSignalName = "ShortSFP_" + CurrentBar;[/INDENT][INDENT]
// Submit a Short entry with a unique name
EnterShort(0, ContractsPerEntry, shortSignalName);
// Print($"[Bar {CurrentBar}] Entered SHORT: {shortSignalName} with stop at {sfpDownHigh}");
// Take Profits are handled in OnExecutionUpdate[/INDENT]
}
protected override void OnExecutionUpdate(Execution execution, string executionId, double price,
int quantity, MarketPosition marketPosition,
string orderId, DateTime time)
{
if (execution.Order == null)
return;
// Only consider filled or partially filled states
if (execution.Order.OrderState != OrderState.Filled &&
execution.Order.OrderState != OrderState.PartFilled)
return;
// Check if it's a Long Entry
if (execution.Order.Name.StartsWith("LongSFP_") && execution.Order.OrderAction == OrderAction.Buy)
{
string fromEntrySignal = execution.Order.Name;
longEntryPrices[fromEntrySignal] = price;
int filledQty = quantity;
longEntryQuantities[fromEntrySignal] = filledQty;
double tp1Price = price + 50;
double tp2Price = price + 75;
double tp3Price = price + 150;
double tp4Price = price + 200;
// Submit partial take-profit limit orders, each with unique signalNames
// and referencing the same fromEntrySignal so they don't cancel each other.
ExitLongLimit(0, true, 1, tp1Price, "TP1_" + fromEntrySignal, fromEntrySignal);
ExitLongLimit(0, true, 1, tp2Price, "TP2_" + fromEntrySignal, fromEntrySignal);
ExitLongLimit(0, true, 1, tp3Price, "TP3_" + fromEntrySignal, fromEntrySignal);
ExitLongLimit(0, true, 1, tp4Price, "TP4_" + fromEntrySignal, fromEntrySignal);
}
// Check if it's a Short Entry
else if (execution.Order.Name.StartsWith("ShortSFP_") && execution.Order.OrderAction == OrderAction.SellShort)
{
string fromEntrySignal = execution.Order.Name;
shortEntryPrices[fromEntrySignal] = price;
int filledQty = quantity;
shortEntryQuantities[fromEntrySignal] = filledQty;
double tp1Price = price - 50;
double tp2Price = price - 75;
double tp3Price = price - 150;
double tp4Price = price - 200;
int contractsPerLimit = filledQty / 4;
ExitShortLimit(0, true, 1, tp1Price, "TP1_" + fromEntrySignal, fromEntrySignal);
ExitShortLimit(0, true, 1, tp2Price, "TP2_" + fromEntrySignal, fromEntrySignal);
ExitShortLimit(0, true, 1, tp3Price, "TP3_" + fromEntrySignal, fromEntrySignal);
ExitShortLimit(0, true, 1, tp4Price, "TP4_" + fromEntrySignal, fromEntrySignal);
}
}
}
}

Comment