Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Inconsistent Multi-Timeframe Indicator

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

    Inconsistent Multi-Timeframe Indicator

    I've built a very simple multi-timeframe indicator. It adds 5 CCI indicators for 1, 5, 10, 15, and 20 minute timeframes. Then it checks if the CCI value for each is under -25 and sets a confirm signal.

    For some reason, it seems to be missing signals. If I print out the value for each CCI, you can easily see all the values are under -25, but there is no signal bar. How can I get this to work?

    See the attached screenshot of the indicator and CCI values. There should be signals for each minute from 22:22 all the way to the first one that actually shows at 22:45, but they are missing.

    Here's the code:
    Code:
    namespace NinjaTrader.NinjaScript.Indicators
    {
    	public class aaMultiCCI : Indicator
    	{
    		private CCI cci0;
    		private CCI cci1;
    		private CCI cci2;
    		private CCI cci3;
    		private CCI cci4;
    		private int shortSignalBar = 0;
    
    		protected override void OnStateChange()
    		{
    			if (State == State.SetDefaults)
    			{
    				Description = @"aaMultiCCI";
    				Name = "aaMultiCCI";
    				Calculate = Calculate.OnBarClose;
    				IsOverlay = false;
    				DisplayInDataBox = true;
    				DrawOnPricePanel = true;
    				DrawHorizontalGridLines = true;
    				DrawVerticalGridLines = true;
    				PaintPriceMarkers = true;
    				ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
    				//Disable this property if your indicator requires custom values that cumulate with each new market data event. 
    				//See Help Guide for additional information.
    				IsSuspendedWhileInactive = true;
    
    				confirmSell = new Series<bool>(this);
    
    				period = 60;
    				cciOverSoldLevel = -25;
    
    				AddPlot(new Stroke(Brushes.Red, 3), PlotStyle.Bar, "confirmSellBar");
    			}
    			else if (State == State.Configure)
    			{
    				AddDataSeries(BarsPeriodType.Minute, 1);
    				AddDataSeries(BarsPeriodType.Minute, 5);
    				AddDataSeries(BarsPeriodType.Minute, 10);
    				AddDataSeries(BarsPeriodType.Minute, 15);
    				AddDataSeries(BarsPeriodType.Minute, 20);
    			}
    			else if (State == State.DataLoaded)
    			{
    				cci0 = CCI(BarsArray[1], period);
    				cci1 = CCI(BarsArray[2], period);
    				cci2 = CCI(BarsArray[3], period);
    				cci3 = CCI(BarsArray[4], period);
    				cci4 = CCI(BarsArray[5], period);
    			}
    		}
    
    		protected override void OnBarUpdate()
    		{
    			if (   CurrentBars[0] < 30 
    				|| CurrentBars[1] < 30 
    				|| CurrentBars[2] < 30 
    				|| CurrentBars[3] < 30 
    				|| CurrentBars[4] < 30
    				|| CurrentBars[5] < 30)
    			{
    				return;
    			}
    
    			if (BarsInProgress != 1)
    			{
    				return;
    			}
    
    			// TODO: Adjust timezone. This is Mountain time.
    			if (Bars.GetTime(CurrentBars[0]) > new DateTime(2017, 11, 08, 22, 20, 00)
    				&& Bars.GetTime(CurrentBars[0]) < new DateTime(2017, 11, 08, 23, 00, 00)
    				)
    			{
    				Print("time: " + Bars.GetTime(CurrentBars[0]));
    				Print("cci0: " + cci0[0]);
    				Print("cci1: " + cci1[0]);
    				Print("cci2: " + cci2[0]);
    				Print("cci3: " + cci3[0]);
    				Print("cci4: " + cci4[0]);
    			}
    
    			if (   cci0[1] < cciOverSoldLevel
    				&& cci1[1] < cciOverSoldLevel
    				&& cci2[1] < cciOverSoldLevel
    				&& cci3[1] < cciOverSoldLevel
    				&& cci4[1] < cciOverSoldLevel
    				)
    			{
    				Values[0][0] = 1;
    				confirmSell[0] = true;
    				shortSignalBar = CurrentBars[0];
    			}
    
    			if (CurrentBars[0] != shortSignalBar)
    			{
    				Values[0].Reset();
    				confirmSell[0] = false;
    			}
    		}
    
    		#region Properties
    
    		[Browsable(false)]
    		[XmlIgnore()]
    		public Series<bool> confirmBuy
    		{ get; set; }
    		[Browsable(false)]
    		[XmlIgnore()]
    		public Series<bool> confirmSell
    		{ get; set; }
    
    		[Range(1, int.MaxValue), NinjaScriptProperty]
    		[Display(ResourceType = typeof(Custom.Resource), Name = "Period", GroupName = "NinjaScriptParameters", Order = 0)]
    		public int period
    		{ get; set; }
    
    		[Range(int.MinValue, 0), NinjaScriptProperty]
    		[Display(ResourceType = typeof(Custom.Resource), Name = "Over Sold Level", GroupName = "NinjaScriptParameters", Order = 1)]
    		public double cciOverSoldLevel
    		{ get; set; }
    
    		#endregion
    	}
    }
    Attached Files
    Last edited by maker; 11-09-2017, 10:12 PM.

    #2
    Hello maker,

    Thanks for your post.

    Your print statements are referring to a different bar than your comparison bar, for example:

    Print("cci1: " + cci1[0]);

    verses

    && cci1[1] < cciOverSoldLevel
    Paul H.NinjaTrader Customer Service

    Comment


      #3
      I'm aware of the index difference. If I change it like below, it will give me signals at 22:30, but I'm still not seeing any signals at 22:22 like I expect:

      Code:
      			// TODO: Adjust timezone. This is Mountain time.
      			if (Bars.GetTime(CurrentBars[0]) > new DateTime(2017, 11, 08, 22, 20, 00)
      				&& Bars.GetTime(CurrentBars[0]) < new DateTime(2017, 11, 08, 23, 00, 00)
      				)
      			{
      				Print("time: " + Bars.GetTime(CurrentBars[0]));
      				Print("cci0: " + cci0[0]);
      				Print("cci1: " + cci1[0]);
      				Print("cci2: " + cci2[0]);
      				Print("cci3: " + cci3[0]);
      				Print("cci4: " + cci4[0]);
      			}
      
      			if (   cci0[1] < cciOverSoldLevel
      				&& cci1[0] < cciOverSoldLevel
      				&& cci2[0] < cciOverSoldLevel
      				&& cci3[0] < cciOverSoldLevel
      				&& cci4[0] < cciOverSoldLevel
      				)
      			{
      				Values[0][0] = 1;
      				confirmSell[0] = true;
      				shortSignalBar = CurrentBars[0];
      			}
      Attached is a screenshot of this code in NT8 and ThinkOrSwim. The ToS indicator has different CCI values and would indicate at 22:22 or in the new screenshot at 6:40.

      I believe the cause of this is because NT8 is seeing the previous bar value for 5, 10, 15 and 20 minute for historical data. This is outlined in "How Bars Data is Referenced".

      Is it possible to build a multi time frame indicator that can use the primary bar series' faster time to give the slow time series bars more granularity? This is something you can set on a strategy with "Order fill resolution: High", but if the strategy uses multiple time frames, it shows an error that it's not supported.

      'High' Order Fill Resolution is only available for single-series strategies. For multi-series strategies, please program directly into your strategy the more granular resolution you would like to simulate order fills with.
      Although this is referring to how orders get filled, it's a similar process to simulating OnEachTick behavior in multi time frame indicators.

      Is there a recommended pattern for this?

      Would doing a second series similar to what is outlined here work?
      Note: In NinjaTrader 8 It is no longer needed to use an indicator to sync a secondary series. This can be done directly from the Series&lt;T&gt; (https://ninjatrader.com/support/helpGuides/nt8/NT%20HelpGuide%20English.html?seriest.htm) constructor. This post is left for historical purposes. Series objects are useful for


      For example, use a 1 minute bar too build my own 20 minute bar series. As each 1 minute bar comes in, update the 20 minute series bar before it closes. This way I could get values like a 20 minute CCI set to OnEachTick, but with historical data.

      There's a post over here that details the issues I'm trying to address with three approaches:
      Yes, you need one counter for each period of chart. Some related example of counting bar volume by cumulating volumes on every tick by bid\\ask side (works on any timeframe min, tick, range, volume etc.) protected override void OnMarketData(MarketDataEventArgs e) { ... tradeSideVolumes.TickVolume = e.Volume; tradeSideVolumes.CumVolume += e.Volume; if (lastBar == CurrentBar) { // we are inside higher timeframe bar tradeSideVolumes.BarVolume += e.Volume; } &#8230;


      What I'm describing above would be this part:
      (2) Calculating from composite bars

      -> it is possible to reproduce the genuine values which would be obtained from the original bar series
      -> composite bars are calculated and stored in arrays, which are similar to DataSeries objects
      -> the array are accessed in a similar way as the DataSeries in standard indicators
      -> when building composite bars, session breaks have to be taken into account
      -> to avoid the drunken sailor path produced by snapshot data during the unstable period of the composite bars, either interpolation (repainting) or smoothing is required
      -> the composite bars do not suffer from the 1-tick lag as above and can easily be called by a strategy
      -> composite bars are limited to the same bar type as the primary bars
      -> the period can only be an integer multiple of the bar period of the primary bars
      -> genuine composite bars can only be built for time-based (day, minute, second), tick or volume charts, but not for range or Renko charts
      -> for range or Renko charts an approxmination is possible via the square root relation ship between range and time
      -> the composite bars approach is relatively CPU intensive
      Attached Files

      Comment


        #4
        Attached is with the updated index code in NT8 and what I'm seeing in ToS. The 22:22 signals are missing.
        Attached Files

        Comment


          #5
          Hello maker,

          Thanks for your reply.

          Correct, when looking at historical data, or when using Calculate mode of OnBarClose, the references will be back to the last completed bar of each series. In the case of 22:30, once you changed your references to [0] you received appropriate indication. In the case of 22:22 you would not receive any indication because the 10 minute bar that closed at 22:20 has the value of 25.36 which you can confirm with the cursor at 22:22 and the data box. The 10-minute bar does not update until 22:30.

          The simple solution here is to use the Calculate mode OnEachTick.which will provide the correct forward references however this would only work with Live data OR playback using Market Replay data. Market replay is the only historical data where the references would be the same as live when using Calculate.OnEachTick.
          Paul H.NinjaTrader Customer Service

          Comment

          Latest Posts

          Collapse

          Topics Statistics Last Post
          Started by jaybedreamin, Today, 05:56 PM
          0 responses
          7 views
          0 likes
          Last Post jaybedreamin  
          Started by DJ888, 04-16-2024, 06:09 PM
          6 responses
          18 views
          0 likes
          Last Post DJ888
          by DJ888
           
          Started by Jon17, Today, 04:33 PM
          0 responses
          4 views
          0 likes
          Last Post Jon17
          by Jon17
           
          Started by Javierw.ok, Today, 04:12 PM
          0 responses
          12 views
          0 likes
          Last Post Javierw.ok  
          Started by timmbbo, Today, 08:59 AM
          2 responses
          13 views
          0 likes
          Last Post bltdavid  
          Working...
          X