Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Two Systems in One Strategy - Display of Trades on Chart

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

    Two Systems in One Strategy - Display of Trades on Chart

    So I'm trying to develop a strategy, using the unmanaged approach, that can effectively run two separate systems in one ninjascript strategy so that I can run these two systems on the same instrument at the same time.

    This is the guidance throughout this forum. Combine multiple systems into one strategy if you want to do this (or use multiple accounts, but I'm trying the combined script approach right now.)

    I ran a simple test. (Strategy code below.)

    * System 1 enters a Long 1 position at 9am.
    * System 2 enters a Long 1 position at 10am.
    * System 2 exits its Long 1 position at 11am.
    * System 1 exits its Long 1 position at 12am.

    The two system trades purposely overlap. System 2 enters and exits in the middle of system 1.

    The chart ends up drawing the trades like this:

    Click image for larger version

Name:	image.png
Views:	86
Size:	33.5 KB
ID:	1327115

    I realize that this is how the strategy position, or the account position, sees these trades.

    Because the System 2 position closes before the System 1 position, it's charted as though the System 2 exit is the exit of the System 1 trade.

    Is there a way to get the chart to display the trades the way the two systems see their trades?

    In this case, our Sys2 trade would be drawn showing it open after Sys1 and close before Sys1.

    Code:
    using NinjaTrader.Cbi;
    using System;
    
    namespace NinjaTrader.NinjaScript.Strategies.Sims
    {
        /// <summary>
        /// This strategy tests using unmanaged market orders to enter two completely
        /// different trades which overlap in time. We want to see how OCO links entries
        /// and exits and how these trades draw on the chart.
        /// </summary>
        public class Sims2SimpleUnmanagedStrategy : Strategy
        {
            Order _entryOrder1;
            Order _entryOrder2;
    
            protected int Quantity => 1;
            protected string System1Name => "Sys1";
            protected string System2Name => "Sys2";
    
            protected override void OnBarUpdate()
            {
                if (CurrentBar < BarsRequiredToTrade) { return; }
    
                OnBarUpdate_System1();
                OnBarUpdate_System2();
            }
            protected void OnBarUpdate_System1()
            {
                if (CurrentBar < BarsRequiredToTrade) { return; }
    
                if ($"{Time[0]:HHmm}" == "0900")
                {
                    SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, Quantity, 0, 0, Guid.NewGuid().ToString(), System1Name);
                }
                else if ($"{Time[0]:HHmm}" == "1200")
                {
                    if (_entryOrder1 != null)
                    {
                        string oco = _entryOrder1.Oco;
                        SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Quantity, 0, 0, oco, System1Name);
                    }
                }
    
            }
            protected void OnBarUpdate_System2()
            {
                if (CurrentBar < BarsRequiredToTrade) { return; }
    
                if ($"{Time[0]:HHmm}" == "1000")
                {
                    SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, Quantity, 0, 0, Guid.NewGuid().ToString(), System2Name);
                }
                else if ($"{Time[0]:HHmm}" == "1100")
                {
                    if (_entryOrder2 != null)
                    {
                        string oco = _entryOrder2.Oco;
                        SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Quantity, 0, 0, oco, System2Name);
                    }
                }
            }
            protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
            {
                Order o = execution.Order;
                bool isExit = execution.IsExit;
                if (isExit && _entryOrder1 != null && _entryOrder1 == o)
                {
                    // Because we restrict Q=1 we know that any filled exit is a full
                    // exit. We don't have to track the total quantity.
                    if (o.Name == System1Name && o.Filled > 0)
                    {
                        _entryOrder1 = null;
                    }
                }
                else if (isExit && _entryOrder2 != null && _entryOrder2 == o)
                {
                    // Because we restrict Q=1 we know that any filled exit is a full
                    // exit. We don't have to track the total quantity.
                    if (o.Name == System2Name && o.Filled > 0)
                    {
                        _entryOrder2 = null;
                    }
                }
            }
            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 (order.Name == System1Name)
                {
                    _entryOrder1 = order;
    
                    // Reset the entryOrder object to null if order was cancelled without any fill
                    if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                    {
                        _entryOrder1 = null;
                    }
                }
                else if (order.Name == System2Name)
                {
                    _entryOrder2 = order;
    
                    // Reset the entryOrder object to null if order was cancelled without any fill
                    if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                    {
                        _entryOrder2 = null;
                    }
                }
            }
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Name = "Sims-2-SimpleUnmanaged";
                    IsInstantiatedOnEachOptimizationIteration = false;
                    EntriesPerDirection = 2;
                    IsExitOnSessionCloseStrategy = false;
                    IsUnmanaged = true;
    
                    _entryOrder1 = null;
                    _entryOrder2 = null;
                }
                else if (State == State.Configure)
                {
                    _entryOrder1 = null;
                    _entryOrder2 = null;
                }
                else if (State == State.DataLoaded)
                {
                    var bp = BarsArray[0].BarsPeriod;
                    if (bp.BarsPeriodType != Data.BarsPeriodType.Minute || bp.Value != 30)
                    {
                        throw new Exception("The primary data series for this test strategy must use 30 minute bars");
                    }
                    if (BarsArray[0].TradingHours.Name != "CME US Index Futures ETH")
                    {
                        throw new Exception("This test strategy requires that the primary data series is using the 'CME US Index Futures ETH' trading hours template.");
                    }
                }
            }
        }
    }
    
    
    ​




    #2
    I found a solution using Draw.Line that appears to work pretty well.

    The only way I found to turn off the current trade lines was to go into data series properties and change the colors to Transparent? Is this the only way? Is there a programmatic way to do this?

    Then I had to track the entry executions. And with each OnExecutionUpdate I evaluate the exit executions and draw the trade line.

    Would this be the recommended way to do this?

    ​​Click image for larger version  Name:	image.png Views:	0 Size:	33.8 KB ID:	1327121

    Code:
    using NinjaTrader.Cbi;
    using NinjaTrader.NinjaScript.DrawingTools;
    using System;
    using System.Windows.Media;
    
    namespace NinjaTrader.NinjaScript.Strategies.Sims
    {
        /// <summary>
        /// This strategy tests using unmanaged market orders to enter two completely
        /// different trades which overlap in time. We want to see how OCO links entries
        /// and exits and how these trades draw on the chart.
        /// </summary>
        public class Sims2SimpleUnmanagedStrategy : Strategy
        {
            private Execution _entryExecution1;
            private Execution _entryExecution2;
            private Order _entryOrder1;
            private Order _entryOrder2;
            private int _tradeCount1;
            private int _tradeCount2;
    
            protected int Quantity => 1;
            protected string System1Name => "Sys1";
            protected string System2Name => "Sys2";
    
            protected override void OnBarUpdate()
            {
                OnBarUpdate_System1();
                OnBarUpdate_System2();
            }
            protected void OnBarUpdate_System1()
            {
                if (CurrentBar < BarsRequiredToTrade) { return; }
    
                if ($"{Time[0]:HHmm}" == "0900")
                {
                    SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, Quantity, 0, 0, "", System1Name);
                }
                else if ($"{Time[0]:HHmm}" == "1200")
                {
                    if (_entryOrder1 != null)
                    {
                        string oco = _entryOrder1.Name;
                        SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Quantity, 0, 0, oco, System1Name);
                    }
                }
            }
            protected void OnBarUpdate_System2()
            {
                if (CurrentBar < BarsRequiredToTrade) { return; }
    
                if ($"{Time[0]:HHmm}" == "1000")
                {
                    SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, Quantity, 0, 0, "", System2Name);
                }
                else if ($"{Time[0]:HHmm}" == "1100")
                {
                    if (_entryOrder2 != null)
                    {
                        string oco = _entryOrder2.Name;
                        SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Quantity, 0, 0, oco, System2Name);
                    }
                }
            }
            protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
            {
                Order o = execution.Order;
                bool isEntry = o.OrderAction == OrderAction.Buy || o.OrderAction == OrderAction.SellShort;
                bool isExit = o.OrderAction == OrderAction.Sell || o.OrderAction == OrderAction.BuyToCover;
                if (_entryOrder1 != null && _entryOrder1 == o)
                {
                    if (isEntry)
                    {
                        _entryExecution1 = execution;
                    }
                    else if (isExit)
                    {
                        // Because we restrict Q=1 we know that any filled exit is a full
                        // exit. We don't have to track the total quantity.
                        if (o.Name == System1Name && o.Filled > 0)
                        {
                            _tradeCount1++;
                            bool isProfitable = CalculateProfit(_entryExecution1, execution) > 0;
                            SolidColorBrush lineColor = isProfitable ? Brushes.DarkGreen : Brushes.Crimson;
                            Draw.Line(this, $"{System1Name} {_tradeCount1}", true, _entryExecution1.Time, _entryExecution1.Price,
                                execution.Time, execution.Order.AverageFillPrice, lineColor, Gui.DashStyleHelper.Dot, 2);
    
                            _entryOrder1 = null;
                            _entryExecution1 = null;
                        }
                    }
                }
                else if (_entryOrder2 != null && _entryOrder2 == o)
                {
                    if (isEntry)
                    {
                        _entryExecution2 = execution;
                    }
                    else if (isExit)
                    {
                        // Because we restrict Q=1 we know that any filled exit is a full
                        // exit. We don't have to track the total quantity.
                        if (o.Name == System2Name && o.Filled > 0)
                        {
                            _tradeCount2++;
                            bool isProfitable = CalculateProfit(_entryExecution2, execution) > 0;
                            SolidColorBrush lineColor = isProfitable ? Brushes.DarkGreen : Brushes.Crimson;
                            Draw.Line(this, $"{System2Name} {_tradeCount2}", true, _entryExecution2.Time, _entryExecution2.Price,
                                execution.Time, execution.Order.AverageFillPrice, lineColor, Gui.DashStyleHelper.Dot, 2);
    
                            _entryOrder2 = null;
                            _entryExecution2 = null;
                        }
                    }
                }
            }
            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 (order.Name == System1Name)
                {
                    _entryOrder1 = order;
    
                    // Reset the entryOrder object to null if order was cancelled without any fill
                    if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                    {
                        _entryOrder1 = null;
                    }
                }
                else if (order.Name == System2Name)
                {
                    _entryOrder2 = order;
    
                    // Reset the entryOrder object to null if order was cancelled without any fill
                    if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                    {
                        _entryOrder2 = null;
                    }
                }
            }
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Name = "Sims-2-SimpleUnmanaged";
                    IsInstantiatedOnEachOptimizationIteration = false;
                    EntriesPerDirection = 2;
                    IsExitOnSessionCloseStrategy = false;
                    IsUnmanaged = true;
                }
                else if (State == State.Configure)
                {
                    _entryOrder1 = null;
                    _entryOrder2 = null;
                    _entryExecution1 = null;
                    _entryExecution2 = null;
                    _tradeCount1 = 0;
                    _tradeCount2 = 0;
                }
                else if (State == State.DataLoaded)
                {
                    var bp = BarsArray[0].BarsPeriod;
                    if (bp.BarsPeriodType != Data.BarsPeriodType.Minute || bp.Value != 30)
                    {
                        throw new Exception("The primary data series for this test strategy must use 30 minute bars");
                    }
                    if (BarsArray[0].TradingHours.Name != "CME US Index Futures ETH")
                    {
                        throw new Exception("This test strategy requires that the primary data series is using the 'CME US Index Futures ETH' trading hours template.");
                    }
                }
            }
    
            private double CalculateProfit(Execution entry, Execution exit)
            {
                if (entry.MarketPosition == MarketPosition.Long)
                {
                    return (exit.Price - entry.Price) * entry.Instrument.MasterInstrument.PointValue - entry.Commission - exit.Commission;
                }
                else
                {
                    return (entry.Price - exit.Price) * entry.Instrument.MasterInstrument.PointValue - entry.Commission - exit.Commission;
                }
            }
        }
    }
    
    
    ​
    Last edited by BarzTrading; 12-09-2024, 09:55 PM.

    Comment


      #3
      Hello BarzTrading,

      Thank you for your post.

      You can disable Plot Executions to remove those text/markers from your chart. Follow these steps:
      • Right-click on your chart, then click Data Series.
      • Under the Properties menu, scroll down to a section called Trades. Left-click to open this menu (if it is not already open).
      • Change the setting called "Plot executions" to "Do not plot".
      • Click the Apply button, then the OK button.
      ​As far as I'm aware there is no way to do this programmatically.

      I would recommend your approach of drawing your own execution lines.
      Gaby V.NinjaTrader Customer Service

      Comment


        #4
        Thanks. I want to keep the default text labels and hide only the trade lines. The text labels are correct. All I want to change are the trade lines.

        Is making them transparent the only way to keep the text labels and markers, but hide the trade lines?

        A new feature request might be to add more options to the Plot executions drop down:
        • Do not plot (current)
        • Markers only (current)
        • Text and markers (current)
        • Markers only, hide trade lines
        • Text and markers, hide trade lines
        Or one could alter the behavior of the current options too for a more consistent and comprehensive set of options. Since these are only UI elements with no programmatic interface, altering the behavior slightly would have a small impact to users:
        • Do not plot (hides text, markers, and trade lines)
        • Markers (draw markers only. No text or trade lines)
        • Text (draw text labels only, No markers or trade lines)
        • Trade lines (draw trade lines only. No text or markers)
        • Markers and text (Draw markers and text, but no trade lines)
        • Markers and trade lines (Draw markers and trade lines, but not text)
        • Text and trade lines (Draw text and trade lines, but not markers)
        • Markers, text, and trade lines (Draw all)


        Comment


          #5
          Hello BarzTrading,

          I see, sorry for misunderstanding. Yes, to keep the labels/markers but not the lines the only way is to make them transparent.

          Thank you for your suggestion. Please use this page to submit this is a feature request: https://support.ninjatrader.com/s/feature-request
          Gaby V.NinjaTrader Customer Service

          Comment

          Latest Posts

          Collapse

          Topics Statistics Last Post
          Started by MikePari, 01-13-2025, 10:36 PM
          2 responses
          14 views
          0 likes
          Last Post MikePari  
          Started by Pa_Baz, 12-14-2024, 09:40 PM
          21 responses
          308 views
          1 like
          Last Post NinjaTrader_LuisH  
          Started by reynoldsn, 01-06-2025, 06:21 PM
          7 responses
          49 views
          0 likes
          Last Post reynoldsn  
          Started by 37s29nala, 05-26-2022, 05:44 PM
          9 responses
          998 views
          0 likes
          Last Post rmhollon  
          Started by TrueFlowTrading, Today, 01:54 PM
          3 responses
          16 views
          0 likes
          Last Post NinjaTrader_ChristopherJ  
          Working...
          X