I want to be able to play a manual order with the default Ninjatrader ATM,
then I want to be able to activate a strategy that will check if Close[0] is near the target by a certain amount of points,
if this occurs, I want to move the stop that was created when I originally placed the order manually.
Is this even possible with Ninjatrader?
Basically I want something similar to chase when touch target, except I want it to trigger before the target is reached.
I can't use the Ninjatrader ATM to do this because I always drag the target according to market conditions,
so I need an automatic way to move the stop when close to the target I've moved my target to.
Basically if Close[0] is within x points of Target orders, Stop order should move to y.
I did this as a test, but even though the move stop code was triggered, it couldn't move the stop.
#region Using declarations using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Xml.Serialization; using NinjaTrader.Cbi; using NinjaTrader.Gui; using NinjaTrader.Gui.Chart; using NinjaTrader.Gui.SuperDom; using NinjaTrader.Gui.Tools; using NinjaTrader.Data; using NinjaTrader.NinjaScript; using NinjaTrader.Core.FloatingPoint; using NinjaTrader.NinjaScript.DrawingTools; using SharpDX; #endregion namespace NinjaTrader.NinjaScript.Strategies { public class MoveMyStopV004 : Strategy { private Account account; [NinjaScriptProperty] [Range(0, double.MaxValue)] [Display(Name = "Target Price", Description = "Fallback target price if no limit order found", Order = 1, GroupName = "Parameters")] public double TargetPrice { get; set; } [NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name = "Stop Offset Ticks", Description = "Ticks below current price for stop", Order = 2, GroupName = "Parameters")] public int StopOffsetTicks { get; set; } [NinjaScriptProperty] [TypeConverter(typeof(AccountNameConverter))] [Display(Name = "Selected Account", Description = "Account to update stop orders", Order = 3, GroupName = "Parameters")] public string SelectedAccount { get; set; } protected override void OnStateChange() { if (State == State.SetDefaults) { Description = "Updates stop market orders for the active position when price is within 10 points of the target price from limit orders."; Name = "MoveMyStopV004"; Calculate = Calculate.OnPriceChange; // Real-time updates IsUnmanaged = true; // For manual order control TargetPrice = 19091.00; // Fallback if no limit order found StopOffsetTicks = 5; // 1.25 points SelectedAccount = "Playback101"; // Default for Market Replay } else if (State == State.Configure) { try { lock (Account.All) { account = Account.All.FirstOrDefault(a => a.Name == SelectedAccount); Print($"[DEBUG] Looking for account {SelectedAccount}: {(account != null ? "Found" : "Not found")}"); } if (account == null) { Print($"[ERROR] Account {SelectedAccount} not found. Ensure it is connected."); } } catch (Exception ex) { Print($"[ERROR] Failed to configure account: {ex.Message}"); } Print($"[DEBUG] Fallback TargetPrice={TargetPrice:F2}, StopOffsetTicks={StopOffsetTicks}, SelectedAccount={SelectedAccount}"); } } protected override void OnBarUpdate() { if (State != State.Realtime || !IsConnected() || account == null) { Print($"[DEBUG] Skipping OnBarUpdate: State={State}, Connected={IsConnected()}, Account={(account == null ? "null" : account.Name)}"); return; } try { // Check for active long position Print($"[DEBUG] Position: MarketPosition={Position.MarketPosition}, Quantity={Position.Quantity}, AveragePrice={Position.AveragePrice:F2}"); if (Position.MarketPosition != MarketPosition.Long) { Print("[DEBUG] No active long position. Skipping stop order updates."); return; } double currentPrice = Close[0]; double tickSize = Instrument.MasterInstrument.TickSize; // Get orders for the active position Order mar****rder = null; Order limitOrder = null; lock (account.Orders) { // Find the most recent filled market buy order mar****rder = account.Orders .Where(o => o.Instrument == Instrument && o.OrderType == OrderType.Market && o.OrderState == OrderState.Filled && o.OrderAction == OrderAction.Buy) .OrderByDescending(o => o.Time) .FirstOrDefault(); // Find the limit sell order (working/accepted) limitOrder = account.Orders .FirstOrDefault(o => o.Instrument == Instrument && o.OrderType == OrderType.Limit && (o.OrderState == OrderState.Working || o.OrderState == OrderState.Accepted) && o.OrderAction == OrderAction.Sell); } // Get target price from limit order double targetPrice = TargetPrice; // Fallback if (limitOrder != null) { targetPrice = limitOrder.LimitPrice; } else { Print("[DEBUG] No working or accepted limit sell order found. Using fallback TargetPrice."); } double distance = Math.Abs(currentPrice - targetPrice); Print($"[DEBUG] CurrentPrice={currentPrice:F2}, TargetPrice={targetPrice:F2}, Distance={distance:F2}, StopOffsetTicks*TickSize={StopOffsetTicks * tickSize:F2}"); lock (account.Orders) { // Log only active position-related orders var activeOrders = account.Orders .Where(o => o.OrderState != OrderState.Cancelled && (o.OrderId == (mar****rder?.OrderId ?? "") || (o.Instrument == Instrument && o.OrderType == OrderType.StopMarket && (o.OrderState == OrderState.Working || o.OrderState == OrderState.Accepted) && o.OrderAction == OrderAction.Sell) || (o.Instrument == Instrument && o.OrderType == OrderType.Limit && (o.OrderState == OrderState.Working || o.OrderState == OrderState.Accepted) && o.OrderAction == OrderAction.Sell))) .ToList(); Print($"[DEBUG] Total active orders in {SelectedAccount} (excluding cancelled): {activeOrders.Count}"); foreach (var order in activeOrders) { string fillPriceLog = order.OrderState == OrderState.Filled ? $", AverageFillPrice={order.AverageFillPrice:F2}" : ""; Print($"[DEBUG] Order {order.OrderId}: Instrument={order.Instrument?.FullName}, Type={order.OrderType}, State={order.OrderState}, Action={order.OrderAction}, StopPrice={order.StopPrice:F2}, LimitPrice={order.LimitPrice:F2}{fillPriceLog}, Quantity={order.Quantity}"); } // Find stop order for debug output var debugStopOrder = activeOrders .FirstOrDefault(o => o.Instrument == Instrument && o.OrderType == OrderType.StopMarket && (o.OrderState == OrderState.Working || o.OrderState == OrderState.Accepted) && o.OrderAction == OrderAction.Sell); Print($"[DEBUG] Stop Order: {(debugStopOrder != null ? $"ID={debugStopOrder.OrderId}, StopPrice={debugStopOrder.StopPrice:F2}" : "None")}"); Print($"[DEBUG] Target Order: {(limitOrder != null ? $"ID={limitOrder.OrderId}, LimitPrice={limitOrder.LimitPrice:F2}" : "None")}"); var stopOrders = account.Orders .Where(o => o.Instrument == Instrument && o.OrderType == OrderType.StopMarket && (o.OrderState == OrderState.Working || o.OrderState == OrderState.Accepted) && o.OrderAction == OrderAction.Sell) .ToList(); Print($"[DEBUG] Found {stopOrders.Count} working or accepted stop market orders for {Instrument.FullName}"); // Log why non-stop orders were excluded var excludedOrders = activeOrders .Where(o => !(o.Instrument == Instrument && o.OrderType == OrderType.StopMarket && (o.OrderState == OrderState.Working || o.OrderState == OrderState.Accepted) && o.OrderAction == OrderAction.Sell)) .ToList(); foreach (var order in excludedOrders) { string reason = ""; if (order.Instrument != Instrument) reason = "Instrument mismatch"; else if (order.OrderType != OrderType.StopMarket) reason = $"Not a stop order: {order.OrderType}"; else if (order.OrderState != OrderState.Working && order.OrderState != OrderState.Accepted) reason = $"Invalid state: {order.OrderState}"; else if (order.OrderAction != OrderAction.Sell) reason = $"Invalid action: {order.OrderAction}"; Print($"[DEBUG] Excluded order {order.OrderId}: {reason}"); } if (stopOrders.Count == 0) { Print("[DEBUG] No working or accepted stop market orders found to update."); return; } foreach (Order stopOrder in stopOrders) { double newStopPrice = 0; string updateReason = ""; // Rule: Within 10 points of target price if (distance <= 10.00) { newStopPrice = currentPrice - (StopOffsetTicks * tickSize); newStopPrice = Instrument.MasterInstrument.RoundToTickSize(newStopPrice); updateReason = "Within 10 points of target"; } else { Print($"[DEBUG] Stop order {stopOrder.OrderId}: No update, Distance={distance:F2} > 10.00"); continue; } if (stopOrder.StopPrice != newStopPrice) { try { ChangeOrder(stopOrder, stopOrder.Quantity, 0, newStopPrice); Print($"[INFO] Updated stop order {stopOrder.OrderId} to {newStopPrice:F2} (previous: {stopOrder.StopPrice:F2}, reason: {updateReason})"); } catch (Exception ex) { Print($"[ERROR] Failed to update stop order {stopOrder.OrderId}: {ex.Message}"); } } else { Print($"[DEBUG] Stop order {stopOrder.OrderId} already at {newStopPrice:F2} (reason: {updateReason})"); } } } } catch (Exception ex) { Print($"[ERROR] Error in OnBarUpdate: {ex.Message}"); } } private bool IsConnected() { return Account.All.Any(a => a.Name == SelectedAccount && a.Connection?.Status == ConnectionStatus.Connected); } } }
Comment