Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Problems adding a trailing exit (not stop) to a strategy

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Problems adding a trailing exit (not stop) to a strategy

    Found a strategy on the forum showing how to use IsAdoptAccountPositionAware to assign stop losses and profit targets to manually entered trades. I'm trying to adapt it to include a trailing exit that triggers on bar close but am running into problems - the strategy disables itself. Much appreciated if someone can help me figure out what I'm doing wrong. Thanks.

    Here's the code:

    Code:
    {
    public class AdoptAccountPositionAndSubmitProtectiveSLPTOrders : Strategy
    {
    
    private bool DoOnceLong = false;
    private bool DoOnceShort = false;
    
    private Order ptLongOrder = null;
    private Order slLongOrder = null;
    private Order ptShortOrder = null;
    private Order slShortOrder = null;
    
    /// <summary>
    /// This strategy will adapt the current account position and then submit a PT limit order and SL Stop order.
    /// Upon either the PT or SL or even a manual order which closes the account position, working SL and PT orders
    /// will be canceled.
    ///
    /// You can manually cancel the orders on chart, and it won't disable strategy.
    ///
    /// Written By Alan Palmer.
    /// </summary>
    
    
    protected override void OnStateChange()
    {
    
    
    
    if (State == State.SetDefaults)
    {
    Description = @"Enter the description for your new custom Strategy here.";
    Name = "AdoptAccountPositionAndSubmitProtectiveSLPTOrders ";
    Calculate = Calculate.OnBarClose;
    EntriesPerDirection = 9;
    EntryHandling = EntryHandling.UniqueEntries;
    IsExitOnSessionCloseStrategy = false;
    ExitOnSessionCloseSeconds = 30;
    IsFillLimitOnTouch = false;
    MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
    OrderFillResolution = OrderFillResolution.Standard;
    Slippage = 0;
    StartBehavior = StartBehavior.AdoptAccountPosition;
    TimeInForce = TimeInForce.Gtc;
    TraceOrders = false;
    RealtimeErrorHandling = RealtimeErrorHandling.IgnoreAllErrors;
    StopTargetHandling = StopTargetHandling.PerEntryExecution;
    BarsRequiredToTrade = 0;
    // Disable this property for performance gains in Strategy Analyzer optimizations
    // See the Help Guide for additional information
    IsInstantiatedOnEachOptimizationIteration = true;
    IsAdoptAccountPositionAware = true;
    Period = 5;
    Multi = 3.5;
    
    
    }
    else if (State == State.Configure)
    {
    }
    }
    
    
    
    
    protected override void OnBarUpdate()
    {
    
    if(State == State.Historical) return;
    
    Print(State.ToString()+PositionsAccount[0].Quantity.ToString());
    Print(PositionsAccount[0].MarketPosition.ToString());
    
    
    
    
    
    ///If account position is long upon starting strategy, submit a PT and SL order for the open position.
    if(PositionsAccount[0].MarketPosition == MarketPosition.Long && DoOnceLong == false)
    {
    double trail;
    double loss = ATR(Input, Period)[0] * Multi;
    
    if (Close[0] > Value[1] && Close[1] > Value[1])
    trail = Math.Max(Value[1], Close[0] - loss);
    
    else if (Close[0] < Value[1] && Close[1] < Value[1])
    trail = Math.Min(Value[1], Close[0] + loss);
    
    else if (Close[0] > Value[1])
    {
    trail = Close[0] - loss;
    }
    
    else
    {
    trail = Close[0] + loss;
    }
    
    Value[0] = trail;
    
    Print("Position is long");
    ExitLongLimit(0, true, PositionsAccount[0].Quantity, Close[0]*1.01,"LongLimitPT", "");
    ExitLongStopMarket(0, true, PositionsAccount[0].Quantity, Close[0]*.99, "StopForLong", "");
    DoOnceLong = true;
    if(Close[0] < trail)
    {
    ExitLong(0, PositionsAccount[0].Quantity, "TrailStopForLong", "");
    }
    }
    
    ///If account position is short upon starting strategy, submit a PT and SL order for the open position.
    if(PositionsAccount[0].MarketPosition == MarketPosition.Short && DoOnceShort ==false)
    {
    Print("Position is short");
    
    ExitShortLimit(0, true, PositionsAccount[0].Quantity, Close[0]*.99,"ShortLimitPT", ""); //Submit PT Limit order for open position
    ExitShortStopMarket(0, true, PositionsAccount[0].Quantity, Close[0]*1.01, "StopForShort", ""); //Submit SL order for open position
    DoOnceShort =true;
    }
    
    ///Should 1 SL or PT or manual order close the position, then need to cancel orders.
    if(PositionsAccount[0].MarketPosition == MarketPosition.Flat)
    {
    Print("Cancel all orders");
    
    if(ptLongOrder != null); //Checking that order object is not null before canceling orders.
    {
    CancelOrder(ptLongOrder); //Cancel ptOrder since we are now flat.
    ptLongOrder=null; //Setting order objects back to null.
    }
    
    if(slLongOrder != null);
    {
    CancelOrder(slLongOrder);
    slLongOrder=null;
    }
    if(ptShortOrder != null);
    {
    CancelOrder(ptShortOrder);
    ptShortOrder=null;
    }
    if(slShortOrder != null);
    {
    CancelOrder(slShortOrder);
    slShortOrder=null;
    }
    }
    
    
    
    }
    
    protected override void OnOrderUpdate(Order order, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, OrderState orderState, DateTime time, ErrorCode error, string nativeError)
    {
    //Assiging order objects to SL and PT for the purpose of canceling orders if the position becomes flat.
    if (order.Name == "LongLimitPT" && orderState != OrderState.Working)
    ptLongOrder = order;
    
    if (order.Name == "StopForLong" && orderState != OrderState.Accepted )
    slLongOrder = order;
    
    
    if (order.Name == "ShortLimitPT" && orderState != OrderState.Working)
    ptShortOrder = order;
    
    if (order.Name == "StopForShort" && orderState != OrderState.Accepted)
    slShortOrder = order;
    
    }
    
    #region Properties
    
    [NinjaScriptProperty]
    [Range(1, int.MaxValue)]
    [Display(Name="Period", Description="ATR period", Order=1, GroupName="Parameters")]
    public int Period
    { get; set; }
    
    [NinjaScriptProperty]
    [Range(1, double.MaxValue)]
    [Display(Name="Multi", Description="ATR multiplication", Order=2, GroupName="Parameters")]
    public double Multi
    { get; set; }
    
    [Browsable(false)]
    [XmlIgnore]
    public Series<double> TrailingStop
    {
    get { return Values[0]; }
    }
    
    #endregion
    
    }
    }

    #2
    Hello mkt_anomalies,

    Thanks for your post.

    I would personally suggest to avoid using PositionsAccount with an Exit method as the Managed Approach is dependent on the strategy position. If the user takes additional entries, the additional entries are not going to be seen in the adopted strategy position, but would be read in PositionsAccount.

    I have an example that works with the strategy position alone which uses AdoptAccountPosition. I have modified it to add a trailing stop. (By trailing stop, I mean a stop order which is used to exit our position.)

    If we refer to our SampleOnOrderUpdate strategy, which the target/stop submission code is based off of, we can see an example of a breakeven in OnBarUpdate. The difference between a trail stop and a breakeven is trivial. A breakeven is when we move the stop loss in association to the average entry price, and a trailing stop is when we move the stop loss in relativity to the to the current/last price.

    I have attached my example for using Adopt Account Position and a modification that demonstrates a trailing stop in OnBarUpdate.

    The code that does the trailing stop can be found here:

    Code:
    if (Position.MarketPosition == MarketPosition.Long && stopOrder != null)
    {
        if (Close[0] >= lastPriceMoved + 5 * TickSize)
        {
            lastPriceMoved = Close[0];
            ChangeOrder(stopOrder, stopOrder.Quantity, 0, Close[0] - 5 * TickSize);
        }
    }
    The strategy examples use the Unmanaged Approach, but ChangeOrder may be used with the Managed Approach as well.

    If you would like to compare the rest of the code against the SampleOnOrderUpdate strategy for some further direction using the Managed Approach for with your own Order object handling, please see below.

    SampleOnOrderUpdate - https://ninjatrader.com/support/help...and_onexec.htm

    We look forward to assisting.
    Attached Files
    JimNinjaTrader Customer Service

    Comment


      #3
      Thanks Jim. Rather than use a conventional trailing stop, I've changed the code to exit the trade using a market order triggered by price moving above/below an ATR Trailing Stop indicator. Do you see any issues with the logic? It seems to work OK in my testing.

      Also if I remove the Target Order and exit the trade manually, how do I get the strategy to check that my position is flat and then cancel any open stop order?




      Code:
      protected override void OnStateChange()
      {
      if (State == State.SetDefaults)
      {
      Description = @"Enter the description for your new custom Strategy here.";
      Name = "AdoptAccountPositionTrailStop";
      Calculate = Calculate.OnBarClose;
      EntriesPerDirection = 1;
      EntryHandling = EntryHandling.AllEntries;
      IsExitOnSessionCloseStrategy = true;
      ExitOnSessionCloseSeconds = 30;
      IsFillLimitOnTouch = false;
      MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
      OrderFillResolution = OrderFillResolution.Standard;
      Slippage = 0;
      TimeInForce = TimeInForce.Gtc;
      TraceOrders = false;
      RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
      StopTargetHandling = StopTargetHandling.PerEntryExecution;
      BarsRequiredToTrade = 20;
      // Disable this property for performance gains in Strategy Analyzer optimizations
      // See the Help Guide for additional information
      IsInstantiatedOnEachOptimizationIteration = true;
      
      IsAdoptAccountPositionAware = true;
      StartBehavior = StartBehavior.AdoptAccountPosition;
      IsUnmanaged = true;
      }
      else if (State == State.Realtime)
      {
      if (Position.MarketPosition == MarketPosition.Long)
      {
      
      oco = GetAtmStrategyUniqueId() + "LongExits";
      stopOrder = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.StopMarket, Position.Quantity, 0, Position.AveragePrice - 20 * TickSize, oco, "StopLossLong");
      // targetOrder = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit, Position.Quantity, Position.AveragePrice + 20 * TickSize, 0, oco, "TargetLong");
      }
      
      if (Position.MarketPosition == MarketPosition.Short)
      {
      double threeBarHigh;
      threeBarHigh = MAX(High, 3)[0];
      
      oco = GetAtmStrategyUniqueId() + "ShortExits";
      stopOrder = SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.StopMarket, Position.Quantity, 0, Position.AveragePrice + 20 * TickSize, oco, "StopLossShort");
      //targetOrder = SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Limit, Position.Quantity, Position.AveragePrice - 20 * TickSize, 0, oco, "TargetShort");
      }
      
      }
      }
      
      protected override void OnBarUpdate()
      {
      
      if (State == State.Historical)
      return;
      
      double trail;
      
      trail = ATRTrailingStop(1, 1, false)[0];
      
      // if (Position.MarketPosition == MarketPosition.Flat && entryOrder == null)
      // {
      // oco = GetAtmStrategyUniqueId() + "entry";
      // SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, 1, 0, 0, oco, "MyEntry");
      // }
      
      
      if (Position.MarketPosition == MarketPosition.Long && stopOrder != null)
      {
      //if (Close[0] >= lastPriceMoved + 5 * TickSize)
      
      if(Close[0] < trail)
      {
      // lastPriceMoved = Close[0];
      // ChangeOrder(stopOrder, stopOrder.Quantity, 0, Close[0] + 5 * TickSize);
      SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, stopOrder.Quantity);
      CancelOrder(stopOrder);
      CancelOrder(targetOrder);
      }
      }
      
      if (Position.MarketPosition == MarketPosition.Short && stopOrder != null)
      {
      //if (Close[0] >= lastPriceMoved + 5 * TickSize)
      
      if(Close[0] > trail)
      {
      // lastPriceMoved = Close[0];
      // ChangeOrder(stopOrder, stopOrder.Quantity, 0, Close[0] - 5 * TickSize);
      SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Market, stopOrder.Quantity);
      CancelOrder(stopOrder);
      CancelOrder(targetOrder);
      }
      }
      }



      Comment


        #4
        Hello mkt_anomalies,

        I would suggest using ChangeOrder to move the stop loss instead of canceling the stop loss and resubmitting, unless you are using TDA which requires you to cancel orders in an OCO pair if you want to move one of those orders.

        However, if the code is working as you would like when testing in realtime and in playback, you could try testing the logic on a paper trading account so orders leave NinjaTrader and you can get a better read on how the strategy will behave when you deploy it live.

        If you are trading futures, you can consider opening a demo account from CQG at the link below and you can connect to that account to place trades to the paper trading account. This will require having a live license key entered in the platform to access the paper trading account (which is seen as an external account/live account to NinjaTrader.)

        A demo account from CQG can be registered here - https://ninjatrader.com/FreeLiveData

        Please note that CQG limits demos to one account per user per year.

        We look forward to assisting.
        JimNinjaTrader Customer Service

        Comment

        Latest Posts

        Collapse

        Topics Statistics Last Post
        Started by bortz, 11-06-2023, 08:04 AM
        47 responses
        1,611 views
        0 likes
        Last Post aligator  
        Started by jaybedreamin, Today, 05:56 PM
        0 responses
        9 views
        0 likes
        Last Post jaybedreamin  
        Started by DJ888, 04-16-2024, 06:09 PM
        6 responses
        19 views
        0 likes
        Last Post DJ888
        by DJ888
         
        Started by Jon17, Today, 04:33 PM
        0 responses
        6 views
        0 likes
        Last Post Jon17
        by Jon17
         
        Started by Javierw.ok, Today, 04:12 PM
        0 responses
        22 views
        0 likes
        Last Post Javierw.ok  
        Working...
        X