/// /// Sample automated strategy for the TDU PATS Indicator /// https://tradedevils-indicators.com/products/tdu-price-action /// using System; using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Windows.Media; using NinjaTrader.Cbi; using NinjaTrader.Core.FloatingPoint; using NinjaTrader.Data; using NinjaTrader.NinjaScript.Indicators.TDU; using NTRes.NinjaTrader.Gui.Tools.Account; namespace NinjaTrader.NinjaScript.Strategies { [TypeConverter("NinjaTrader.NinjaScript.Strategies.PATSStrategyConvertor")] public class PATSExampleStrategy : Strategy { private Trade _currentTrade; private TDUPriceAction _patsIndicator; private int _prevTradeBar; #region OnStateChange & OnBarUpdate protected override void OnStateChange() { base.OnStateChange(); if (State == State.SetDefaults) { Name = "PATS Example Strategy"; Description = "PATS Example Strategy"; Calculate = Calculate.OnEachTick; RealtimeErrorHandling = RealtimeErrorHandling.IgnoreAllErrors; IsExitOnSessionCloseStrategy = false; EntriesPerDirection = 1000; EntryHandling = EntryHandling.AllEntries; IsUnmanaged = true; MaximumBarsLookBack = MaximumBarsLookBack.Infinite; // 256 no longer supported StartBehavior = StartBehavior.WaitUntilFlat; //PATS settings LegCountMethod = TDUPatsRules.Mack; ResetCountAtDTDB = true; StopLossTicks = 1; // position sizing ScalpPositionFixedContracts = 1; RunnerPositionFixedContracts = 1; // targets ScalpTargetTicks = 4; RunnerTargetTicks = 16; MoveToBreakEven = true; } else if (State == State.Configure) { AddDataSeries(BarsPeriodType.Tick, 1); } else if (State == State.DataLoaded) { _patsIndicator = TDUPriceAction(Close, LegCountMethod, ResetCountAtDTDB, TDUPatsTradeManagement.Internal, 0, 1, 100, 1, TDUPATSPositionSizing.Contracts, 1, 100, 1, 1, TDUPATSPositionSizingRunner.None, 1, 1, 1, 1); _patsIndicator.Show01 = true; _patsIndicator.ShowTraps = false; AddChartIndicator(_patsIndicator); } } protected override void OnBarUpdate() { if (CurrentBar < BarsRequiredToTrade) { return; } if (BarsInProgress != 0) { return; } ManageTrades(); OpenNewTrades(); } #endregion #region entry rules private int GetSignalBar(bool isLong) { for (var i = 0; i < 10; ++i) { if (isLong) { if (High[i] > High[i + 1]) { var lowestPrice = Low[i] < Low[i + 1] ? Low[i] : Low[i + 1]; var barsAgo = Low[i] < Low[i + 1] ? i : i + 1; for (var x = barsAgo; x < 10; ++x) { if (High[x + 1] > High[x]) { break; } if (Low[x] < lowestPrice) { lowestPrice = Low[x]; barsAgo = x; } } // NinjaTrader.NinjaScript.DrawingTools.Draw.Dot(this, "high" + _signalDrawIndex, false, barsAgo, Low[barsAgo], Brushes.Lime); return barsAgo; } } else { var highestPrice = High[i] > High[i + 1] ? High[i] : High[i + 1]; var barsAgo = High[i] > High[i + 1] ? i : i + 1; for (var x = barsAgo; x < 10; ++x) { if (Low[x + 1] < Low[x]) { break; } if (High[x] > highestPrice) { highestPrice = Low[x]; barsAgo = x; } } // NinjaTrader.NinjaScript.DrawingTools.Draw.Dot(this, "low" + _signalDrawIndex, false, barsAgo, High[barsAgo], Brushes.Crimson); return barsAgo; } } return 0; } private bool OpenLongTrade() { if (_patsIndicator.SignalLong[0].ApproxCompare(1) == 0) { var high = Math.Max(High[0], High[1]); var entryPrice = high + TickSize; var signalBar = GetSignalBar(true); var stopLossPrice = Low[signalBar] - StopLossTicks * TickSize; var scalpContracts = GetContracts(true, entryPrice, stopLossPrice); if (scalpContracts > 0) { Print("\n"); _currentTrade = new Trade { EntryBar = CurrentBar, StoplossPrice = stopLossPrice, ScalpTargetPrice = entryPrice + ScalpTargetTicks * TickSize, RunnerTargetPrice = entryPrice + RunnerTargetTicks * TickSize }; Print(string.Format("{0} scalp long entry:{1} SL:{2} TP:{3} ", CurrentBar, entryPrice, stopLossPrice, _currentTrade.ScalpTargetPrice)); _currentTrade.ScalpEntry = SubmitOrderUnmanaged(1, OrderAction.Buy, OrderType.StopMarket, scalpContracts, 0, entryPrice, "", "Long Scalp"); var runnerContracts = GetContracts(false, entryPrice, stopLossPrice); if (runnerContracts > 0) { Print(string.Format("{0} runner long entry:{1} SL:{2} TP:{3} ", CurrentBar, entryPrice, stopLossPrice, _currentTrade.ScalpTargetPrice)); _currentTrade.RunnerEntry = SubmitOrderUnmanaged(1, OrderAction.Buy, OrderType.StopMarket, runnerContracts, 0, entryPrice, "", "Long Runner"); } } } return false; } private bool OpenShortTrade() { // Check for 2ES short if (_patsIndicator.SignalShort[0].ApproxCompare(-1) == 0) { var low = Math.Min(Low[0], Low[1]); var entryPrice = low - TickSize; var signalBarsAgo = GetSignalBar(false); var stopLossPrice = High[signalBarsAgo] + StopLossTicks * TickSize; var scalpContracts = GetContracts(true, entryPrice, stopLossPrice); if (scalpContracts > 0) { Print("\n"); _currentTrade = new Trade { EntryBar = CurrentBar, StoplossPrice = stopLossPrice, ScalpTargetPrice = entryPrice - ScalpTargetTicks * TickSize, RunnerTargetPrice = entryPrice - RunnerTargetTicks * TickSize }; Print(string.Format("{0} scalp short entry:{1} SL:{2} TP:{3} ", CurrentBar, entryPrice, stopLossPrice, _currentTrade.ScalpTargetPrice)); _currentTrade.ScalpEntry = SubmitOrderUnmanaged(1, OrderAction.Sell, OrderType.StopMarket, scalpContracts, 0, entryPrice, "", "Short Scalp"); var runnerContracts = GetContracts(false, entryPrice, stopLossPrice); if (runnerContracts > 0) { Print(string.Format("{0} runner short entry:{1} SL:{2} TP:{3} ", CurrentBar, entryPrice, stopLossPrice, _currentTrade.ScalpTargetPrice)); _currentTrade.RunnerEntry = SubmitOrderUnmanaged(1, OrderAction.Sell, OrderType.StopMarket, runnerContracts, 0, entryPrice, "", "Short Runner"); } } } return false; } #endregion #region trade management private void OpenNewTrades() { if (_currentTrade == null) { if (CurrentBar != _prevTradeBar) { if (CanOpenTrade(true)) { if (OpenLongTrade()) { _prevTradeBar = CurrentBar; } } } } if (_currentTrade == null) { if (CurrentBar != _prevTradeBar) { if (CanOpenTrade(false)) { if (OpenShortTrade()) { _prevTradeBar = CurrentBar; } } } } } protected override void OnOrderUpdate(Order order, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, OrderState orderState, DateTime time, ErrorCode error, string comment) { if (_currentTrade == null) { return; } var pointValue = Instrument.MasterInstrument.PointValue; var tickAmount = pointValue * TickSize; // see https://ninjatrader.com/support/helpGuides/nt8/?advanced_order_handling.htm#Transitioningorderreferencesfromhistoricaltolive if (_currentTrade.ScalpEntry != null && _currentTrade.ScalpEntry.IsBacktestOrder && State == State.Realtime) _currentTrade.ScalpEntry = GetRealtimeOrder(_currentTrade.ScalpEntry); if (_currentTrade.RunnerEntry != null && _currentTrade.RunnerEntry.IsBacktestOrder && State == State.Realtime) _currentTrade.RunnerEntry = GetRealtimeOrder(_currentTrade.RunnerEntry); if (_currentTrade.ScalpStoploss != null && _currentTrade.ScalpStoploss.IsBacktestOrder && State == State.Realtime) _currentTrade.ScalpStoploss = GetRealtimeOrder(_currentTrade.ScalpStoploss); if (_currentTrade.ScalpTarget != null && _currentTrade.ScalpTarget.IsBacktestOrder && State == State.Realtime) _currentTrade.ScalpTarget = GetRealtimeOrder(_currentTrade.ScalpTarget); if (_currentTrade.RunnerStoploss != null && _currentTrade.RunnerStoploss.IsBacktestOrder && State == State.Realtime) _currentTrade.RunnerStoploss = GetRealtimeOrder(_currentTrade.RunnerStoploss); if (_currentTrade.RunnerTarget != null && _currentTrade.RunnerTarget.IsBacktestOrder && State == State.Realtime) _currentTrade.RunnerTarget = GetRealtimeOrder(_currentTrade.RunnerTarget); // handle scalp entry order fill if (order == _currentTrade.ScalpEntry || order.Name == "Long Scalp" || order.Name == "Short Scalp") { _currentTrade.ScalpEntry = order; if (order.Filled == _currentTrade.ScalpEntry.Quantity) { if (_currentTrade.ScalpStoploss == null || _currentTrade.ScalpTarget == null) { // create SL/TP orders if (order.IsLong) { Print(string.Format("{0} scalp long entry:{1} filled. Set SL:{2} / TP:{3} cl:{4} {5}", CurrentBar, order.AverageFillPrice, _currentTrade.StoplossPrice, _currentTrade.ScalpTargetPrice, Closes[0][0], _patsIndicator.StopLossLong[0])); _currentTrade.ScalpStoploss = SubmitOrderUnmanaged(1, OrderAction.Sell, OrderType.StopMarket, order.Quantity, 0, _currentTrade.StoplossPrice, _currentTrade.ScalpOcoId, "Long Scalp SL"); _currentTrade.ScalpTarget = SubmitOrderUnmanaged(1, OrderAction.Sell, OrderType.Limit, order.Quantity, _currentTrade.ScalpTargetPrice, 0, _currentTrade.ScalpOcoId, "Long Scalp TP"); } else { Print(string.Format("{0} scalp short entry:{1} filled. Set SL:{2} / TP:{3} cl:{4} {5}", CurrentBar, order.AverageFillPrice, _currentTrade.StoplossPrice, _currentTrade.ScalpTargetPrice, Closes[0][0], _patsIndicator.StopLossShort[0])); _currentTrade.ScalpStoploss = SubmitOrderUnmanaged(1, OrderAction.Buy, OrderType.StopMarket, order.Quantity, 0, _currentTrade.StoplossPrice, _currentTrade.ScalpOcoId, "Short Scalp SL"); _currentTrade.ScalpTarget = SubmitOrderUnmanaged(1, OrderAction.Buy, OrderType.Limit, order.Quantity, _currentTrade.ScalpTargetPrice, 0, _currentTrade.ScalpOcoId, "Short Scalp TP"); } } } } // handle runner entry order fill if (order == _currentTrade.RunnerEntry || order.Name == "Long Runner" || order.Name == "Short Runner") { _currentTrade.RunnerEntry = order; if (order.Filled == _currentTrade.RunnerEntry.Quantity) { if (_currentTrade.RunnerStoploss == null || _currentTrade.RunnerTarget == null) { // create SL/TP orders if (order.IsLong) { Print(string.Format("{0} Runner long entry:{1} filled. Set SL:{2} / TP:{3} cl:{4}", CurrentBar, order.AverageFillPrice, _currentTrade.StoplossPrice, _currentTrade.RunnerTargetPrice, Closes[0][0])); _currentTrade.RunnerStoploss = SubmitOrderUnmanaged(1, OrderAction.Sell, OrderType.StopMarket, order.Quantity, 0, _currentTrade.StoplossPrice, _currentTrade.RunnerOcoId, "Long Runner SL"); _currentTrade.RunnerTarget = SubmitOrderUnmanaged(1, OrderAction.Sell, OrderType.Limit, order.Quantity, _currentTrade.RunnerTargetPrice, 0, _currentTrade.RunnerOcoId, "Long Runner TP"); } else { Print(string.Format("{0} Runner short entry:{1} filled. Set SL:{2} / TP:{3} cl:{4}", CurrentBar, order.AverageFillPrice, _currentTrade.StoplossPrice, _currentTrade.RunnerTargetPrice, Closes[0][0])); _currentTrade.RunnerStoploss = SubmitOrderUnmanaged(1, OrderAction.Buy, OrderType.StopMarket, order.Quantity, 0, _currentTrade.StoplossPrice, _currentTrade.RunnerOcoId, "Short Runner SL"); _currentTrade.RunnerTarget = SubmitOrderUnmanaged(1, OrderAction.Buy, OrderType.Limit, order.Quantity, _currentTrade.RunnerTargetPrice, 0, _currentTrade.RunnerOcoId, "Short Runner TP"); } } } } // handle scalp stoploss if (order == _currentTrade.ScalpStoploss || order.Name == "Long Scalp SL" || order.Name == "Short Scalp SL") { _currentTrade.ScalpStoploss = order; if (order.Filled == _currentTrade.ScalpStoploss.Quantity) { // we hit the stoploss, so entire trade is done if (_currentTrade.RunnerStoploss != null && _currentTrade.RunnerStoploss.Filled == 0) { _currentTrade.RunnerStoploss.Filled = _currentTrade.RunnerStoploss.Quantity; } Print(string.Format("{0} scalp stoploss hit {1} ", CurrentBar, order.AverageFillPrice)); _currentTrade = null; return; } } // handle runner stoploss if (order == _currentTrade.RunnerStoploss || order.Name == "Long Runner SL" || order.Name == "Short Runner SL") { _currentTrade.RunnerStoploss = order; if (order.Filled == _currentTrade.RunnerStoploss.Quantity) { Print(string.Format("{0} runner stoploss hit {1} ", CurrentBar, order.AverageFillPrice)); if (_currentTrade != null && (_currentTrade.ScalpStoploss.Filled != 0 || _currentTrade.ScalpTarget.Filled != 0)) { // we hit the stoploss, so entire trade is done _currentTrade = null; return; } } } // handle scalp target if (order == _currentTrade.ScalpTarget || order.Name == "Long Scalp TP" || order.Name == "Short Scalp TP") { _currentTrade.ScalpTarget = order; if (order.Filled == _currentTrade.ScalpEntry.Quantity) { Print(string.Format("{0} scalp target hit {1} ", CurrentBar, order.AverageFillPrice)); if (_currentTrade == null) return; // we hit the scalp TP if (_currentTrade.RunnerEntry == null) { // no runner , so done _currentTrade = null; return; } else { // move runner stop to Break even if (_currentTrade.RunnerStoploss != null) { Print(string.Format("{0} move runner stoploss to breakeven {1}.", CurrentBar, _currentTrade.RunnerEntry.StopPrice)); _currentTrade.RunnerStoploss.StopPriceChanged = _currentTrade.RunnerEntry.StopPrice; ChangeOrder(_currentTrade.RunnerStoploss, _currentTrade.RunnerStoploss.Quantity, 0, _currentTrade.RunnerEntry.StopPrice); } } } } // handle runner target if (order == _currentTrade.RunnerTarget || order.Name == "Long Runner TP" || order.Name == "Short Runner TP") { _currentTrade.RunnerTarget = order; if (order.Filled == _currentTrade.RunnerTarget.Quantity) { // we hit the Runner TP // no runner , so done Print(string.Format("{0} Runner target hit {1} ", CurrentBar, order.AverageFillPrice)); _currentTrade = null; return; } } } private void ManageTrades() { if (BarsInProgress != 0) return; if (CurrentBar < 5) return; if (_currentTrade == null) return; if (CurrentBar == _currentTrade.EntryBar) return; // trail scalp entry as long as not filled if (_currentTrade.ScalpEntry != null && _currentTrade.ScalpEntry.Filled == 0) { var isLong = _currentTrade.ScalpEntry.IsLong; var signalBarsAgo = GetSignalBar(isLong); if (!CanOpenTrade(isLong)) { _prevTradeBar = CurrentBar; Print(string.Format("{0} cancel order due to filter", CurrentBar)); CancelOrder(_currentTrade.ScalpEntry); if (_currentTrade.RunnerEntry != null) CancelOrder(_currentTrade.RunnerEntry); _currentTrade = null; return; } var entryPrice = isLong ? High[1] + TickSize : Low[1] - TickSize; signalBarsAgo = GetSignalBar(isLong); _currentTrade.StoplossPrice = isLong ? Low[signalBarsAgo] - StopLossTicks * TickSize : High[signalBarsAgo] + StopLossTicks * TickSize; _currentTrade.ScalpTargetPrice = isLong ? entryPrice + ScalpTargetTicks * TickSize : entryPrice - ScalpTargetTicks * TickSize; var contracts = GetContracts(true, entryPrice, _currentTrade.StoplossPrice); contracts = Math.Max(contracts, 1); if (_currentTrade.ScalpEntry.StopPrice.ApproxCompare(entryPrice) != 0) { Print(string.Format("{0} scalp trail entry {1} sl:{2} q:{3}", CurrentBar, entryPrice, _currentTrade.StoplossPrice, contracts)); _currentTrade.ScalpEntry.StopPriceChanged = entryPrice; _currentTrade.ScalpEntry.QuantityChanged = contracts; ChangeOrder(_currentTrade.ScalpEntry, contracts, 0, entryPrice); } } // trail runner entry as long as not filled if (_currentTrade.RunnerEntry != null && _currentTrade.RunnerEntry.Filled == 0) { var entryPrice = _currentTrade.RunnerEntry.IsLong ? Highs[0][1] + TickSize : Lows[0][1] - TickSize; _currentTrade.RunnerTargetPrice = _currentTrade.RunnerEntry.IsLong ? entryPrice + RunnerTargetTicks * TickSize : entryPrice - RunnerTargetTicks * TickSize; var contracts = GetContracts(false, entryPrice, _currentTrade.StoplossPrice); contracts = Math.Max(contracts, 1); if (_currentTrade.RunnerEntry.StopPrice.ApproxCompare(entryPrice) != 0) { Print(string.Format("{0} runner trail entry {1} sl:{2} q:{3}", CurrentBar, entryPrice, _currentTrade.StoplossPrice, contracts)); _currentTrade.RunnerEntry.StopPriceChanged = entryPrice; ChangeOrder(_currentTrade.RunnerEntry, _currentTrade.RunnerEntry.Quantity, 0, entryPrice); } } // trail runner // check if scalp target hit if (_currentTrade.ScalpTarget != null && _currentTrade.ScalpTarget.Filled == _currentTrade.ScalpTarget.Quantity) { // check if we have a runner if (_currentTrade.RunnerEntry != null && _currentTrade.RunnerEntry.Filled == _currentTrade.RunnerEntry.Quantity) { if (_currentTrade.RunnerStoploss == null) return; if (_currentTrade.RunnerTarget == null) return; if (_currentTrade.RunnerStoploss.Filled > 0) return; if (_currentTrade.RunnerTarget.Filled > 0) return; var newStoploss = _currentTrade.RunnerStoploss.StopPrice; var currentStoploss = _currentTrade.RunnerStoploss.StopPrice; if (_currentTrade.RunnerEntry.IsLong) { newStoploss = Math.Max(Low[1] - 1 * TickSize, currentStoploss); } else { newStoploss = Math.Min(High[1] + 1 * TickSize, currentStoploss); } newStoploss = Instrument.MasterInstrument.RoundToTickSize(newStoploss); if (_currentTrade.RunnerStoploss.StopPrice.ApproxCompare(newStoploss) != 0) { if ((_currentTrade.RunnerEntry.IsLong && newStoploss < Lows[0][0]) || (!_currentTrade.RunnerEntry.IsLong && newStoploss > Highs[0][0])) { Print(string.Format("{0} runner trail stoploss {1} ", CurrentBar, newStoploss)); _currentTrade.RunnerStoploss.StopPriceChanged = newStoploss; ChangeOrder(_currentTrade.RunnerStoploss, _currentTrade.RunnerStoploss.Quantity, 0, newStoploss); } } } } } private int GetContracts(bool scalp, double openPrice, double stoplossPrice) { return scalp ? ScalpPositionFixedContracts : RunnerPositionFixedContracts; } private bool CanOpenTrade(bool isLong) { if (BarsInProgress != 0) return false; if (CurrentBar < 5) return false; var time = Times[0][0]; return true; } #endregion #region helper classes protected class Trade { public Trade() { ScalpOcoId = Guid.NewGuid().ToString(); RunnerOcoId = Guid.NewGuid().ToString(); } public Order ScalpEntry { get; set; } public Order ScalpStoploss { get; set; } public Order ScalpTarget { get; set; } public Order RunnerEntry { get; set; } public Order RunnerStoploss { get; set; } public Order RunnerTarget { get; set; } public int EntryBar { get; set; } public double StoplossPrice { get; set; } public double ScalpTargetPrice { get; set; } public string ScalpOcoId { get; set; } public double RunnerTargetPrice { get; set; } public string RunnerOcoId { get; set; } public double GetTradeProfit(double tickSize, double tickValue, double commission, bool scalp) { if (ScalpEntry == null) return 0; var openPrice = ScalpEntry.StopPrice; var closePrice = 0d; var lots = 0; if (scalp) { lots = ScalpEntry.Quantity; if (ScalpTarget.Filled > 0) closePrice = ScalpTarget.AverageFillPrice; else closePrice = ScalpStoploss.StopPrice; } else { lots = RunnerEntry.Quantity; if (RunnerTarget.Filled > 0) closePrice = RunnerTarget.AverageFillPrice; else closePrice = RunnerStoploss.StopPrice; } var priceMove = ScalpEntry.IsLong ? closePrice - openPrice : openPrice - closePrice; var ticks = priceMove / tickSize; var profit = ticks * tickValue; return profit * lots - commission * lots; } public void CloseAllOrders(Strategy strategy) { CloseOrders(strategy, ScalpEntry, ScalpStoploss, ScalpTarget); CloseOrders(strategy, RunnerEntry, RunnerStoploss, RunnerTarget); } private void CloseOrders(Strategy strategy, Order entry, Order stoploss, Order target) { if (entry == null) return; if (entry.Filled == 0) { strategy.CancelOrder(entry); if (stoploss != null) strategy.CancelOrder(stoploss); if (target != null) strategy.CancelOrder(target); return; } if (stoploss != null && stoploss.Filled > 0) return; if (target != null && target.Filled > 0) return; if (stoploss != null) strategy.CancelOrder(stoploss); if (target != null) strategy.CancelOrder(target); var action = entry.IsLong ? OrderAction.Sell : OrderAction.BuyToCover; strategy.SubmitOrderUnmanaged(1, action, OrderType.Market, entry.Quantity, 0, 0, "", "exit"); } } #endregion #region properties [ReadOnly(true)] [Display(ResourceType = typeof(Custom.Resource), Name = "Indicator Version", GroupName = "00. TradeDevils", Order = 0)] public string Version { get { return "1.0.0.1"; } set { } } [ReadOnly(true)] [Display(ResourceType = typeof(Custom.Resource), Name = "Website", GroupName = "00. TradeDevils", Order = 1)] public string Website { get { return "www.tradedevils-indicators.com"; } set { } } //---------------------------------- [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = "Leg count method", GroupName = "01. PATS Parameters", Order = 0)] public TDUPatsRules LegCountMethod { get; set; } [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = "Reset count at DT/DB", GroupName = "01. PATS Parameters", Order = 1)] public bool ResetCountAtDTDB { get; set; } [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = "Stoploss # ticks above/below signal bar", Description = "Number of ticks above/below signal bar", GroupName = "01. PATS Parameters", Order = 2)] public int StopLossTicks { get; set; } //-------------------------------------------------------------------------------------------------------------------------------- [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = "Scalp # Contracts", GroupName = "02. Position sizing", Order = 2)] public int ScalpPositionFixedContracts { get; set; } [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = "Runner # Contracts", GroupName = "02. Position sizing", Order = 12)] public int RunnerPositionFixedContracts { get; set; } //-- [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = " Scalp Target (Ticks)", GroupName = "03. Take Profit", Order = 1)] public int ScalpTargetTicks { get; set; } [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = " Runner Target (Ticks)", GroupName = "03. Take Profit", Order = 2)] public int RunnerTargetTicks { get; set; } [NinjaScriptProperty] [Display(ResourceType = typeof(Custom.Resource), Name = " Move Runner to breakeven when Scalp Hit target", GroupName = "03. Take Profit", Order = 3)] public bool MoveToBreakEven { get; set; } #endregion } }