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