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

stop limit (mkt) order 1 tick above previous bar highest bid+offer volume price

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

    #16
    Hello Marble,

    You would need to review the code surrounding the two variables being used "sortedDicList" and "cacheDictionary".

    You can copy the related code using those variables to accumulate volume into the dictionaries. You would not need the OnRender code, that is just for its custom display. This indicator uses OnMarketData to accumulate the data in realtime, for it to work historically you can use TickReplay for estimating the volume on historical bars. TickReplay does not playback all ask/bid events but only the Last events and the ask/bid at the time of the last so it would not be a complete picture like you get in realtime.

    Once you have the accumulated volume in the dictionaries you can later reuse that data from OnBarUpdate in whatever way you wanted. To know which price had the highest volume you would need to add your own logic which iterates the cacheDictionary to find which price had the highest volume. There is some code which sorts the volume already in that script however it is not for the use you are asking about, that logic is shown in OnRender. You can reference that code from OnRender to see how to iterate the dictionary and use the elements in the dictionary. Dictionaries are a part of C# and not specific to NinjaScript so there is a lot of education information that you can find online surrounding how to use dictionaries if that subject is foggy.

    To summarize, if you directly copied the code you would need the following areas of code copied to your script to accumulate the volume at price information:

    internal class VolumeInfoItem
    OnStateChange -> State.DataLoaded code
    GetLastBarSessionDate
    OnMarketData

    The following variables would be needed for the method GetLastBarSessionDate and the OnMarketData code:
    private SessionIterator sessionIterator;
    private DateTime sessionDateTmp = Globals.MinDate;
    private SessionIterator storedSession;
    private DateTime cacheSessionEnd = Globals.MinDate;
    private DateTime sessionDateTmp = Globals.MinDate;
    private List<int> newSessionBarIdx = new List<int>();
    ​private List<Dictionary<double, VolumeInfoItem>> sortedDicList = new List<Dictionary<double, VolumeInfoItem>>();
    private Dictionary<double, VolumeInfoItem> cacheDictionary = new Dictionary<double, VolumeInfoItem>();
    ​private DateTime currentDate = Globals.MinDate;
    ​private double bidPrice;
    private double askPrice;


    Rather than directly copying the code you may find it easier to learn from the concepts shown in OnMarketData because you really only need the data from cacheDictionary. You can further simplify the code being used by eliminating the extra code which is used to render the indicator in its custom way. The concept shown of how to accumulate the volume at price using a dictionary is the important part. The OnMarketData code is how the ask/bid prices and volumes get mapped into a dictionary.

    A dictionary uses keys to store data, you can use a key which is a price and then the value for that key is the volume at that price. To simplify the code in that indicator you can remove the class VolumeInfoItem and instead just apply the volume as the value which would be a variable that looks like this:

    Code:
    private Dictionary<double, long> cacheDictionary = new Dictionary<double, long>();

    Using a dictionary like this would look like:

    Code:
    if (!cacheDictionary.ContainsKey(price))
    cacheDictionary.Add(price, volume);


    JesseNinjaTrader Customer Service

    Comment


      #17
      Hi Jesse, Thank you for the detail. I am trying to update based on your suggestion.
      **I am trying to use this strategy under tick play turned ON mode.

      1. internal class VolumeInfoItem > added
      Code:
      //internal class VolumeInfoItem from volume profile
              internal class VolumeInfoItem
              {
                  public double up;
                  public double down;
                  public double neutral;
              }
              private SessionIterator sessionIterator;
              private DateTime sessionDateTmp = Globals.MinDate;
              private SessionIterator storedSession;
              private DateTime cacheSessionEnd = Globals.MinDate;
              private DateTime sessionDateTmp = Globals.MinDate;
              private List&lt;int&gt; newSessionBarIdx = new List&lt;int&gt;();
              //​private List&lt;Dictionary&lt;double, VolumeInfoItem&gt;&gt; sortedDicList = new List&lt;Dictionary&lt;double, VolumeInfoItem&gt;&gt;();
      ​        private List&lt;Dictionary&lt;double,long&gt;&gt; sortedDicList = new List&lt;Dictionary&lt;double, long&gt;&gt;();
      
              //private Dictionary&lt;double, VolumeInfoItem&gt; cacheDictionary = new Dictionary&lt;double, VolumeInfoItem&gt;();
              private Dictionary&lt;double, long&gt; cacheDictionary = new Dictionary&lt;double, long&gt;();
      
              ​private DateTime currentDate = Globals.MinDate;
              ​private double bidPrice;
              private double askPrice;
      
              // add GetLastBarSessionDate​
      2. OnStateChange ->State.DataLoaded code &gt;&gt; where to add this? I added as below "if (State == State.DataLoaded)" but it does not seems right.
      Code:
              protected override void OnStateChange()
              {
                  // OnStateChange -&gt; State.DataLoaded code
                  if (State == State.DataLoaded) //added
                  if (State == State.SetDefaults)
                  {​

      3. GetLastBarSessionDate &gt;&gt;added

      4 OnMarketData
      I have replaced VolumeInfoItem simply with volume,
      Can I delete below volumeinfoItem deleted?
      Code:
      volumeInfoItem = cacheDictionary[price];
      
                          if (price &gt;= e.Ask)
                              volumeInfoItem.up        += volume;
                          else if (price &lt;= e.Bid)
                              volumeInfoItem.down        += volume;
                          else
                              volumeInfoItem.neutral    += volume;
                      }​
      protected override void OnMarketData(MarketDataEventArgs e)
      {
      if (Bars.Count &lt;= 0)
      return;

      double price;
      long volume;
      //VolumeInfoItem volumeInfoItem;
      DateTime lastBarTimeStamp = GetLastBarSessionDate(Time[0]);

      if (lastBarTimeStamp != currentDate)
      {
      //cacheDictionary = new Dictionary&lt;double, VolumeInfoItem&gt;();
      cacheDictionary = new Dictionary&lt;double,long&gt;();
      sortedDicList.Add(cacheDictionary);
      }

      currentDate = lastBarTimeStamp;
      if (Bars.IsTickReplay)
      {
      if (e.MarketDataType == MarketDataType.Last)
      {
      price = e.Price;
      volume = e.Volume;

      if (!cacheDictionary.ContainsKey(price))
      //cacheDictionary.Add(price, new VolumeInfoItem());
      cacheDictionary.Add(price, volume);

      volumeInfoItem = cacheDictionary[price]; //can be deleted?

      if (price &gt;= e.Ask)//can be deleted?
      volumeInfoItem.up += volume;//can be deleted?
      else if (price &lt;= e.Bid)
      volumeInfoItem.down += volume;//can be deleted?
      else
      volumeInfoItem.neutral += volume//can be deleted?
      }
      }
      else
      {
      if (e.MarketDataType == MarketDataType.Ask)
      {
      askPrice = e.Price;
      return;
      }

      if (e.MarketDataType == MarketDataType.Bid)
      {
      bidPrice = e.Price;
      return;
      }

      if (e.MarketDataType != MarketDataType.Last || ChartControl == null || askPrice == 0 || bidPrice == 0)
      return;

      if (Bars != null &amp;&amp; !SessionIterator.IsInSession(Core.Globals.Now, true, true))
      return;

      price = e.Price;
      volume = e.Volume;

      //if (!cacheDictionary.ContainsKey(price))
      // cacheDictionary.Add(price, new VolumeInfoItem());

      //Simplify above code
      if (!cacheDictionary.ContainsKey(price))
      cacheDictionary.Add(price, volume);//simplified

      volumeInfoItem = cacheDictionary[price];//can be deleted?

      if (price &gt;= askPrice)
      volumeInfoItem.up += volume;//can be deleted?
      else if (price &lt;= bidPrice)
      volumeInfoItem.down += volume;//can be deleted?
      else
      volumeInfoItem.neutral += volume;
      }//can be deleted?
      }​
      Also I encounter below errors. But it seems all are declared.​
      Click image for larger version  Name:	1.png Views:	0 Size:	11.2 KB ID:	1216744


      Thank you again for all the assistance..
      Marble
      Last edited by Marble; 09-23-2022, 09:25 AM.

      Comment


        #18
        Hello Marble,

        I looked at the script however you have quite a few errors. If you wanted to start with that code it would be easiest to open the exiting indicator and then Right click -> Save as to make a duplicate. I would suggest using an indicator as a starting point so that you can easily reload it while you make changes, once you have the logic you wanted you could migrate what you have into a strategy.

        Another note is that the VolumeInfoItem used in the dictionary does not contain any volume data, that contains some double numbers which is how the original indicator renders. You would need to copy that for the original code to work to start with but that won't be what you need to use the dictionary for volume information. As mentioned in my last post you would end up using a dictionary like the following where you could set an amount of volume for each price.

        Code:
        private Dictionary<double, long> cacheDictionary = new Dictionary<double, long>();

        The original indicator can be used as a reference to see how to use dictionaries in general and also how you work with volume at price data but it won't be a simple copy/paste to get that working. You will need to make some modifications to use the dictionary how you wanted.

        If you have not worked with dictionaries in C# I would suggest doing some external learning on that topic first so it is easier to understand what the original indicator is doing. Here is one public resource that contains samples of using a dictionary: https://www.geeksforgeeks.org/c-shar...with-examples/

        JesseNinjaTrader Customer Service

        Comment


          #19
          Hello.
          Noted to step back and start from the indicator. I have saved under new name.

          I am only using the provious candle highest volume price. With this indicator, I want visually confirm the Volume Profile for the previous bar.
          Which code to modify if I only want to see previous bar (one bar) volume profile instead of volume profile from the first visible bar?

          Code:
          namespace NinjaTrader.NinjaScript.Indicators
          {
              public class VolProfPrevBar : Indicator
              {
                  #region Properties
                  internal class VolumeInfoItem
                  {
                      public double up;
                      public double down;
                      public double neutral;
                  }
          
                  private double            alpha                = 50;
                  private double            askPrice;
                  private int                barSpacing            = 1;
                  private double            bidPrice;
                  private DateTime        cacheSessionEnd        = Globals.MinDate;
                  private DateTime        currentDate            = Globals.MinDate;
                  private bool            drawLines;
                  private List<int>        newSessionBarIdx    = new List<int>();
                  private DateTime        sessionDateTmp        = Globals.MinDate;
                  private SessionIterator sessionIterator;
                  private int                startIndexOf;
                  private SessionIterator storedSession;
          
                  private List<Dictionary<double, VolumeInfoItem>>     sortedDicList   = new List<Dictionary<double, VolumeInfoItem>>();
                  private Dictionary<double, VolumeInfoItem>             cacheDictionary = new Dictionary<double, VolumeInfoItem>();
                  #endregion
          
                  protected override void OnStateChange()
                  {
                      if (State == State.SetDefaults)
                      {
                          Description                    = NinjaTrader.Custom.Resource.NinjaScriptIndicatorDescriptionVolumeProfile;
                          Name                        = "VolProfPrevBar";
                          Calculate                    = Calculate.OnEachTick;
                          DrawLines                    = false;
                          IsChartOnly                    = true;
                          IsOverlay                    = true;
                          DrawOnPricePanel            = false;
                          LineBrush                    = Brushes.DarkGray;
                          VolumeDownBrush                = Brushes.Crimson;
                          VolumeNeutralBrush            = Brushes.DarkGray;
                          VolumeUpBrush                = Brushes.DarkCyan;
                      }
                      else if (State == State.Configure)
                      {
                          ZOrder = -1;
                      }
                      else if (State == State.DataLoaded)
                      {
                          storedSession = new SessionIterator(Bars);
                      }
                      else if (State == State.Historical)
                      {
                          if (Calculate != Calculate.OnEachTick)
                              Draw.TextFixed(this, "NinjaScriptInfo", string.Format(NinjaTrader.Custom.Resource.NinjaScriptOnBarCloseError, Name), TextPosition.BottomRight);
                      }
                  }
          
                  protected override void OnBarUpdate() { }
          
                  private DateTime GetLastBarSessionDate(DateTime time)
                  {
                      if (time <= cacheSessionEnd)
                          return sessionDateTmp;
          
                      if (!Bars.BarsType.IsIntraday)
                          return sessionDateTmp;
          
                      storedSession.GetNextSession(time, true);
          
                      cacheSessionEnd = storedSession.ActualSessionEnd;
                      sessionDateTmp     = TimeZoneInfo.ConvertTime(cacheSessionEnd.AddSeconds(-1), Globals.GeneralOptions.TimeZoneInfo, Bars.TradingHours.TimeZoneInfo);
          
                      if(newSessionBarIdx.Count == 0 || newSessionBarIdx.Count > 0 && CurrentBar > newSessionBarIdx[newSessionBarIdx.Count - 1])
                          newSessionBarIdx.Add(CurrentBar);
          
                      return sessionDateTmp;
                  }
          
                  protected override void OnMarketData(MarketDataEventArgs e)
                  {
                      if (Bars.Count <= 0)
                          return;
          
                      double            price;
                      long            volume;
                      VolumeInfoItem    volumeInfoItem;
                      DateTime        lastBarTimeStamp = GetLastBarSessionDate(Time[0]);
          
                      if (lastBarTimeStamp != currentDate)
                      {
                          cacheDictionary = new Dictionary<double, VolumeInfoItem>();
                          sortedDicList.Add(cacheDictionary);
                      }
          
                      currentDate = lastBarTimeStamp;
                      if (Bars.IsTickReplay)
                      {
                          if (e.MarketDataType == MarketDataType.Last)
                          {
                              price    = e.Price;
                              volume    = e.Volume;
          
                              if (!cacheDictionary.ContainsKey(price))
                                  cacheDictionary.Add(price, new VolumeInfoItem());
          
                              volumeInfoItem = cacheDictionary[price];
          
                              if (price >= e.Ask)
                                  volumeInfoItem.up        += volume;
                              else if (price <= e.Bid)
                                  volumeInfoItem.down        += volume;
                              else
                                  volumeInfoItem.neutral    += volume;
                          }
                      }
                      else
                      {
                          if (e.MarketDataType == MarketDataType.Ask)
                          {
                              askPrice = e.Price;
                              return;
                          }
          
                          if (e.MarketDataType == MarketDataType.Bid)
                          {
                              bidPrice = e.Price;
                              return;
                          }
          
                          if (e.MarketDataType != MarketDataType.Last || ChartControl == null || askPrice == 0 || bidPrice == 0)
                              return;
          
                          if (Bars != null && !SessionIterator.IsInSession(Core.Globals.Now, true, true))
                              return;
          
                          price    = e.Price;
                          volume    = e.Volume;
          
                          if (!cacheDictionary.ContainsKey(price))
                              cacheDictionary.Add(price, new VolumeInfoItem());
          
                          volumeInfoItem = cacheDictionary[price];
          
                          if (price >= askPrice)
                              volumeInfoItem.up        += volume;
                          else if (price <= bidPrice)
                              volumeInfoItem.down        += volume;
                          else
                              volumeInfoItem.neutral    += volume;
                      }
                  }
          
                  protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                  {
                      if(Bars == null || Bars.Instrument == null || IsInHitTest)
                          return;
          
                      int        firstBarIdxToPaint    = -1;
                      double    tickSize            = Bars.Instrument.MasterInstrument.TickSize;
                      double    volumeMax            = 0;
          
                      SharpDX.Direct2D1.Brush upBrush            = VolumeUpBrush.ToDxBrush(RenderTarget);
                      SharpDX.Direct2D1.Brush downBrush        = VolumeDownBrush.ToDxBrush(RenderTarget);
                      SharpDX.Direct2D1.Brush neutralBrush    = VolumeNeutralBrush.ToDxBrush(RenderTarget);
                      SharpDX.Direct2D1.Brush lineBrushDx        = LineBrush.ToDxBrush(RenderTarget);
          
                      upBrush.Opacity            = (float)(alpha / 100.0);
                      downBrush.Opacity        = (float)(alpha / 100.0);
                      neutralBrush.Opacity    = (float)(alpha / 100.0);
          
                      for (int i = newSessionBarIdx.Count - 1; i > 0; i--)
                      {
                          if (newSessionBarIdx[i] <= ChartBars.ToIndex)
                          {
                              startIndexOf        = i;
                              firstBarIdxToPaint    = newSessionBarIdx[i];
                              break;
                          }
                      }
          
                      if (sortedDicList.Count < 1 && cacheDictionary.Keys.Count > 0)
                          sortedDicList.Add(cacheDictionary);
          
                      foreach (Dictionary<double, VolumeInfoItem> tmpDict in sortedDicList)
                      {
                          foreach (KeyValuePair<double, VolumeInfoItem> keyValue in tmpDict)
                          {
                              double price = keyValue.Key;
          
                              if (Bars.BarsType.IsIntraday && (price > chartScale.MaxValue || price < chartScale.MinValue))
                                  continue;
          
                              VolumeInfoItem    vii        = keyValue.Value;
                              double            total    = vii.up + vii.down + vii.neutral;
                              volumeMax                = Math.Max(volumeMax, total);
                          }
                      }
          
                      if (volumeMax.ApproxCompare(0) == 0)
                          return;
          
                      int viiPositions = 0;
          
                      foreach (KeyValuePair<double, VolumeInfoItem> keyValue in sortedDicList[startIndexOf])
                      {
                          viiPositions++;
          
                          VolumeInfoItem vii = keyValue.Value;
          
                          double    priceLower            = keyValue.Key - tickSize / 2;
                          float    yLower                = chartScale.GetYByValue(priceLower);
                          float    yUpper                = chartScale.GetYByValue(priceLower + tickSize);
                          float    height                = Math.Max(1, Math.Abs(yUpper - yLower) - barSpacing);
                          int        barWidthUp            = (int)((ChartPanel.W / 2) * (vii.up / volumeMax));
                          int        barWidthNeutral        = (int)((ChartPanel.W / 2) * (vii.neutral / volumeMax));
                          int        barWidthDown        = (int)((ChartPanel.W / 2) * (vii.down / volumeMax));
                          float    stationaryXpos        = chartControl.GetXByBarIndex(ChartBars, !Bars.IsTickReplay ? ChartBars.FromIndex : Math.Max(ChartBars.FromIndex, firstBarIdxToPaint));
                          float    xpos                = chartControl.GetXByBarIndex(ChartBars, !Bars.IsTickReplay ? ChartBars.FromIndex : Math.Max(1, Math.Max(ChartBars.FromIndex, firstBarIdxToPaint)) - 1);
          
                          RenderTarget.FillRectangle(new SharpDX.RectangleF(xpos, yUpper, barWidthUp, height), upBrush);
                          xpos += barWidthUp;
                          RenderTarget.FillRectangle(new SharpDX.RectangleF(xpos, yUpper, barWidthNeutral, height), neutralBrush);
                          xpos += barWidthNeutral;
                          RenderTarget.FillRectangle(new SharpDX.RectangleF(xpos, yUpper, barWidthDown, height), downBrush);
          
                          if (!drawLines)
                              continue;
          
                          // Lower line
                          RenderTarget.DrawLine(new SharpDX.Vector2(stationaryXpos, yLower), new SharpDX.Vector2((ChartPanel.X + ChartPanel.W), yLower), lineBrushDx);
          
                          // Upper line (only at very top)
                          if (viiPositions == sortedDicList[startIndexOf].Count)
                              RenderTarget.DrawLine(new SharpDX.Vector2(stationaryXpos, yUpper), new SharpDX.Vector2((ChartPanel.X + ChartPanel.W), yUpper), lineBrushDx);
                      }
          
                      lineBrushDx.Dispose();
                      upBrush.Dispose();
                      downBrush.Dispose();
                      neutralBrush.Dispose();
                  }
          ​
          Thank you
          Marble
          Attached Files

          Comment


            #20
            Hello Marble,

            The original indicator does not store the previous bars data, it works from the incoming market data and displays the current session data.

            To work with individual bars you would need to implement logic similar to the BuySellVolume indicator which collects data from OnMarketData into a series by using OnBarUpdate. If you wanted to know the volume profile data for a previous bar that would increase the complexity of the changes you would need to make.

            As a starting point I would suggest that you work with the current bar data and get the script to the point where you are finding the data you wanted from the custom dictionary. Again your not going to be able to use the existing VolumeInfoItem dictionary for that purpose, you would instead need a Dictionary<double, long> where you can store total volume at price. You can then find a maximum volume from the dictionary or do whatever other tasks you needed.

            To store that into a series so you can use BarsAgo to get the last bars data you would need to add reset logic for each bar like the BuySellVolume indicator in OnBarUpdate. You would also need a Series<Dictionary<double, long>> for that purpose to store the volume profile data per bar. This will require some programming on your part to make and test those changes.
            JesseNinjaTrader Customer Service

            Comment


              #21
              Jesse

              I am very confused with your suggestion/instruction and now dont know where to start the modification of original volume profile.
              Please advise step by step. To avoid unnecessary reworks I will post modification to ensure I am on the right track.
              As a first step, I want to visually confirm each bar hightest volume price.

              As for dictionary, I understood the concept but not clear how to accomodate in volume profile.
              Attached is the dictionary I could understand from other thread which seems aligned to your suggestion but not sure how to accomodate.
              And I could not visually confirm using this indicator.

              Can you please instruct first step to modify volume profile or buysell indicator (if this is more appropriate)I need this to be completed even it takes time. Thank you
              Marble​

              Comment


                #22
                Hello Marble,

                That looks like you are on the right track for using the dictionaries and also resetting per bar. To do the ask/bid accumulation you can refer to the buysellvolume indicator as well, that part is the OnMarketData logic. The incoming market data is stored in the series so that it can be referenced using a bars ago. You could use what you have posted and add OnMarketData to support accumulating volumes at prices to the dictionaries and then set that result to the series you made.

                To use the dictionaries for accumulating volume along with the series the basic steps would be:

                In OnBarUpdate create new dictionaries for each bar just like you are now
                In OnMarketData access the series variables so you can check if the dictionary has the key which is the OnMarketData price. If it has no price then set the price as the key and the volume as the value
                If it does have that key already then use the series variable to get any existing volume from the dictionary at that price. Add the current events volume to that amount, then set it to the dictionary as the value for that price.

                This would let you accumulate a volume at each price so that you can later work with the volume at price information for each bar.

                JesseNinjaTrader Customer Service

                Comment


                  #23
                  Jesse

                  You have confused me more. which part of my posting are you refeering. there is dictionary in buysellvolumewithdctionary file as well as original volume profile.
                  Also you have mentioned infoitem in volume profile is not needed. what do you mean?
                  What do you mean by use "a bar ago"

                  again, I am very confused with your suggestion and unclear to what to modify the volume profile indicator file to visually show volume profile only for previous bar


                  That looks like you are on the right track for using the dictionaries and also resetting per bar. To do the ask/bid accumulation you can refer to the buysellvolume indicator as well, that part is the OnMarketData logic. The incoming market data is stored in the series so that it can be referenced using a bars ago. You could use what you have posted and add OnMarketData to support accumulating volumes at prices to the dictionaries and then set that result to the series you made.

                  To use the dictionaries for accumulating volume along with the series the basic steps would be:

                  In OnBarUpdate create new dictionaries for each bar just like you are now
                  In OnMarketData access the series variables so you can check if the dictionary has the key which is the OnMarketData price. If it has no price then set the price as the key and the volume as the value
                  If it does have that key already then use the series variable to get any existing volume from the dictionary at that price. Add the current events volume to that amount, then set it to the dictionary as the value for that price.

                  This would let you accumulate a volume at each price so that you can later work with the volume at price information for each bar.​

                  Comment


                    #24
                    Hello Marble,

                    which part of my posting are you refeering.
                    I was referring to the zip file you posted in post 21

                    Also you have mentioned infoitem in volume profile is not needed. what do you mean?
                    That is for the specific use in the volume profile, because your not trying to draw a profile in the same way that class is not needed. I mentioned that you don't need to copy the volume profile directly but learn from what it does. That is one way to accumulate volume using the OnMarketData override. In your use case you just need a dictionary of type <double, long> so you can store a <price, volume> in the dictionary.

                    What do you mean by use "a bar ago"
                    ​You asked to get the previous bars value, BarsAgo is how you reference previous bar data in a series. This is just like using the basic price series such as Close[1] or Close[BarsAgo]. Creating a custom series works in the same way you can access previous bars series values.

                    again, I am very confused with your suggestion and unclear to what to modify the volume profile indicator file to visually show volume profile only for previous bar
                    ​Did you create the zip file in post 21 or was that downloaded from somewhere? The zip file in post 21 is very close to what I was describing however it uses OnBarUpdate and a 1 tick series to calculate the volumes at price. You can use that type of logic or you can do something like the BuySellVolume or VolumeProfile which specifically use OnMarketData to do the accumulation. Either way as long as the calculation is using 1 tick granularity and you have access to the ask/bid volumes you will be able to accumulate that into the series that's being used.

                    The sample you provided is closer to what you want to do so that is likely a better starting point. If you are not sure how its logic works that would be one of the purposes of using prints. You can explore that logic on your end to see how it works and then you can judge what you want to add to do other tasks you need. That script could serve for a basis of finding previous bars ask/bid volume at price.


                    JesseNinjaTrader Customer Service

                    Comment


                      #25
                      Hello Jesse,

                      ​Did you create the zip file in post 21 or was that downloaded from somewhere? The zip file in post 21 is very close to what I was describing however it uses OnBarUpdate and a 1 tick series to calculate the volumes at price. You can use that type of logic or you can do something like the BuySellVolume or VolumeProfile which specifically use OnMarketData to do the accumulation. Either way as long as the calculation is using 1 tick granularity and you have access to the ask/bid volumes you will be able to accumulate that into the series that's being used.

                      The sample you provided is closer to what you want to do so that is likely a better starting point. If you are not sure how its logic works that would be one of the purposes of using prints. You can explore that logic on your end to see how it works and then you can judge what you want to add to do other tasks you need. That script could serve for a basis of finding previous bars ask/bid volume at price.​
                      I did not create this file attached. NinjaTrader_Support Jim created this file as response to other users question (in #4).
                      https://ninjatrader.com/support/foru...om-the-example


                      OnBarUpdate and a 1 tick series to calculate the volumes at price. You can use that type of logic or you can do something like the BuySellVolume or VolumeProfile which specifically use OnMarketData to do the accumulation.
                      1. I will take this file as a starting point as you suggested. But I want to use OnMarketData. Which part do I need to change. I need highest bid+ask volume rather than tick. please refer a code that I can refer may be Volume profile indicator?

                      2. I can not visually confirm the highest volme (bid+ask) price. how can I thought it would be better to set a dot symbil marker to identify the highest volume trader price for each bar to ensure the price specified in the code is correct. how can I set a dot to plot the hightest price per bar?

                      Code:
                      [
                      
                      //This namespace holds Indicators in this folder and is required. Do not change it.
                      namespace NinjaTrader.NinjaScript.Indicators
                      {
                      public class BuySellVolumeOneTickDictionaries : Indicator
                      {
                      private Series<Dictionary<double, double>> MySeriesDictAsk;
                      private Series<Dictionary<double, double>> MySeriesDictBid;
                      
                      private Dictionary<double, double> TempAskDict;
                      private Dictionary<double, double> TempBidDict;
                      
                      protected override void OnStateChange()
                      {
                      if (State == State.SetDefaults)
                      {
                      Description = @"Demonstration of OF+ Best Practices in BuySellVolume, using Dictionaries!";
                      Name = "BuySellVolumeOneTickDictionaries";
                      Calculate = Calculate.OnBarClose;
                      IsOverlay = false;
                      DisplayInDataBox = true;
                      DrawOnPricePanel = true;
                      DrawHorizontalGridLines = true;
                      DrawVerticalGridLines = true;
                      PaintPriceMarkers = true;
                      ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                      IsSuspendedWhileInactive = true;
                      
                      AddPlot(new Stroke(Brushes.DarkCyan, 2), PlotStyle.Bar, "BuyVolume");
                      AddPlot(new Stroke(Brushes.Crimson, 2), PlotStyle.Bar, "SellVolume");
                      
                      AddPlot(new Stroke(Brushes.DarkCyan, 2), PlotStyle.Bar, "BuyVolume2");
                      AddPlot(new Stroke(Brushes.Crimson, 2), PlotStyle.Bar, "SellVolume2");
                      }
                      else if (State == State.Configure)
                      {
                      AddDataSeries(BarsPeriodType.Tick, 1);
                      }
                      else if (State == State.DataLoaded)
                      {
                      MySeriesDictAsk = new Series<Dictionary<double, double>>(this, MaximumBarsLookBack.Infinite);
                      MySeriesDictBid = new Series<Dictionary<double, double>>(this, MaximumBarsLookBack.Infinite);
                      
                      TempAskDict = new Dictionary<double, double>();
                      TempBidDict = new Dictionary<double, double>();
                      }
                      }
                      
                      private double buys = 1;
                      private double sells = 1;
                      
                      private bool isReset;
                      
                      private int lastBar;
                      private bool lastInTransition;
                      
                      protected override void OnBarUpdate()
                      {
                      if (BarsInProgress == 0)
                      {
                      // This lets us know what processing mode we are in
                      // if indexOffset == 0 then we are in 'realtime processing mode'
                      // if indexOffset is > 0 then we are in 'historical processing mode'
                      int indexOffset = BarsArray[1].Count - 1 - CurrentBars[1];
                      
                      // If we are not Calculate.OnBarClose and we are in Realtime processing mode
                      if (IsFirstTickOfBar && Calculate != Calculate.OnBarClose && (State == State.Realtime || BarsArray[0].IsTickReplay))
                      {
                      // We always get the last tick after the primary triggers OBU so we update the last bar
                      if (CurrentBars[0] > 0)
                      SetValues(1);
                      
                      // We have the last tick of the bar added so now we can reset
                      if (BarsArray[0].IsTickReplay || State == State.Realtime && indexOffset == 0)
                      ResetValues(false);
                      }
                      
                      // We only set the value on the primary to preserve external programmatic access to plot as well as indicator-as-input cases
                      SetValues(0);
                      
                      // If we are Calculate.OnBarClose or we are in Historical processing mode, we are already update to date on the 1 tick series so we reset here
                      if (Calculate == Calculate.OnBarClose || (lastBar != CurrentBars[0] && (State == State.Historical || State == State.Realtime && indexOffset > 0)))
                      ResetValues(false);
                      
                      lastBar = CurrentBars[0];
                      
                      }
                      else if (BarsInProgress == 1)
                      {
                      // The more granular series will open the new session so we have to reset any session related stuff here
                      if (BarsArray[1].IsFirstBarOfSession)
                      ResetValues(true);
                      
                      // We only calculate values from the 1 tick series
                      CalculateValues(false);
                      }
                      }
                      
                      private void CalculateValues(bool forceCurrentBar)
                      {
                      // This lets us know what processing mode we are in
                      // if indexOffset == 0 and State is Realtime then we are in 'realtime processing mode'
                      // if indexOffset is > 0 then we are in 'historical processing mode'
                      int indexOffset = BarsArray[1].Count - 1 - CurrentBars[1];
                      bool inTransition = State == State.Realtime && indexOffset > 1;
                      
                      // For Calculate.OnBarClose in realtime processing we have to advance the index on the tick series to not be one tick behind
                      // The means, at the end of the 'transition' (where State is Realtime but we are still in historical processing mode) -> we have to calculate two ticks (CurrentBars[1] and CurrentBars[1] + 1)
                      if (!inTransition && lastInTransition && !forceCurrentBar && Calculate == Calculate.OnBarClose)
                      CalculateValues(true);
                      
                      bool useCurrentBar = State == State.Historical || inTransition || Calculate != Calculate.OnBarClose || forceCurrentBar;
                      
                      // This is where we decide what index to use
                      int whatBar = useCurrentBar ? CurrentBars[1] : Math.Min(CurrentBars[1] + 1, BarsArray[1].Count - 1);
                      
                      // This is how we get the right tick values
                      double volume = BarsArray[1].GetVolume(whatBar);
                      double price = BarsArray[1].GetClose(whatBar);
                      
                      // Accumulate volume
                      if (price >= BarsArray[1].GetAsk(whatBar))
                      {
                      buys += volume;
                      if (TempAskDict.ContainsKey(price))
                      TempAskDict[price] += volume;
                      else
                      TempAskDict.Add(price, volume);
                      }
                      else if (price <= BarsArray[1].GetBid(whatBar))
                      {
                      sells += volume;
                      if (TempBidDict.ContainsKey(price))
                      TempBidDict[price] += volume;
                      else
                      TempBidDict.Add(price, volume);
                      }
                      
                      lastInTransition = inTransition;
                      }
                      
                      private void SetValues(int barsAgo)
                      {
                      // Typical assignment for BuySellVolume
                      BuyVolume[barsAgo] = buys + sells;
                      SellVolume[barsAgo] = sells;
                      
                      // When using Dictionaries, make sure we create a new empty dictionaries to store values from our TempDictionaries
                      MySeriesDictAsk[barsAgo] = new Dictionary<double, double>();
                      MySeriesDictBid[barsAgo] = new Dictionary<double, double>();
                      
                      // Populate values from the TempAsk Dictionary to the bar's Ask Dictionary
                      foreach (KeyValuePair<double, double> kvp in TempAskDict)
                      MySeriesDictAsk[barsAgo].Add(kvp.Key, kvp.Value);
                      
                      // Populate values from the TempBid Dictionary to the bar's Bid Dictionary
                      foreach (KeyValuePair<double, double> kvp in TempBidDict)
                      MySeriesDictBid[barsAgo].Add(kvp.Key, kvp.Value);
                      
                      // Be sure to reset our accumulation of Ask Volume from Dictionary
                      double dictAskTotalVolume = 0;
                      foreach (KeyValuePair<double, double> kvp in MySeriesDictAsk[barsAgo])
                      dictAskTotalVolume += kvp.Value;
                      
                      // Assign our accumulation of Ask Volume to the plot
                      Values[2][barsAgo] = dictAskTotalVolume;
                      
                      // Be sure to reset our accumulation of Bid Volume from Dictionary
                      double dictBidTotalVolume = 0;
                      foreach (KeyValuePair<double, double> kvp in MySeriesDictBid[barsAgo])
                      dictBidTotalVolume += kvp.Value;
                      
                      // Assign our accumulation of Bid Volume to the plot
                      Values[3][barsAgo] = dictBidTotalVolume;
                      
                      // Correct the Buy Volume plot to hold Buy and Sell Volume
                      Values[2][barsAgo] = Values[2][barsAgo] + Values[3][barsAgo];
                      
                      }
                      
                      private void ResetValues(bool isNewSession)
                      {
                      buys = sells = 0;
                      
                      isReset = true;
                      
                      TempAskDict.Clear();
                      TempBidDict.Clear();
                      
                      if (isNewSession)
                      {
                      // Cumulative values (per session) would reset here
                      // EMA is not gapless so we ignore in this example
                      }
                      }
                      
                      [Browsable(false)]
                      [XmlIgnore()]
                      public Series<double> BuyVolume
                      {
                      get { return Values[0]; }
                      }
                      
                      [Browsable(false)]
                      [XmlIgnore()]
                      public Series<double> SellVolume
                      {
                      get { return Values[1]; }
                      }
                      }
                      }
                      
                      [HASHTAG="t3322"]region[/HASHTAG] NinjaScript generated code. Neither change nor remove.
                      
                      namespace NinjaTrader.NinjaScript.Indicators
                      {
                      public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
                      {
                      private BuySellVolumeOneTickDictionaries[] cacheBuySellVolumeOneTickDictionaries;
                      public BuySellVolumeOneTickDictionaries BuySellVolumeOneTickDictionaries()
                      {
                      return BuySellVolumeOneTickDictionaries(Input);
                      }
                      
                      public BuySellVolumeOneTickDictionaries BuySellVolumeOneTickDictionaries(ISeries<double> input)
                      {
                      if (cacheBuySellVolumeOneTickDictionaries != null)
                      for (int idx = 0; idx < cacheBuySellVolumeOneTickDictionaries.Length; idx++)
                      if (cacheBuySellVolumeOneTickDictionaries[idx] != null && cacheBuySellVolumeOneTickDictionaries[idx].EqualsInput(input))
                      return cacheBuySellVolumeOneTickDictionaries[idx];
                      return CacheIndicator<BuySellVolumeOneTickDictionaries>(n ew BuySellVolumeOneTickDictionaries(), input, ref cacheBuySellVolumeOneTickDictionaries);
                      }
                      
                      ​
                      Attached Files
                      Last edited by Marble; 09-29-2022, 02:27 AM.

                      Comment


                        #26
                        Hello Marble,

                        Which part do I need to change.
                        I would suggest to explore this sample further before trying to make the OnMarketData change as it will work in the same way by making that change. The script is adding a secondary 1 tick series which would be updating the series on each tick just like OnMarketData.

                        If you wanted to use OnMarketData then you would move the accumulation logic from OnBarUpdate to OnMarketData. You still need the series logic inside of OnBarUpdate. The way that script works is that it uses the primary series to store data and 1 tick intervals to accumulate data. It separates that logic in OnBarUpdate using the BarsInProgress index.

                        You would need logic that is similar to the following in OnMarketData, this is just the existing OnBarUpdate accumulation which would need modified to work with the OnMarketData variables.

                        Code:
                        // Accumulate volume
                        if (price >= BarsArray[1].GetAsk(whatBar))
                        {
                        buys += volume;
                        if (TempAskDict.ContainsKey(price))
                        TempAskDict[price] += volume;
                        else
                        TempAskDict.Add(price, volume);
                        }
                        else if (price <= BarsArray[1].GetBid(whatBar))
                        {
                        sells += volume;
                        if (TempBidDict.ContainsKey(price))
                        TempBidDict[price] += volume;
                        else
                        TempBidDict.Add(price, volume);
                        }



                        I need highest bid+ask volume rather than tick
                        If you mean highest bid or ask volume at a price then the dictionaries can be used for that. If you mean highest bid+ask volume on a single bar the script also does that using its BuyVolume plot.

                        If you look in the SetValues method that shows the dictionarires and how the volume accumulation is happening. The MySeriesDictAsk MySeriesDictBid are used to store the price/volume infomration per bar. You can access the values in MySeriesDictAsk/Bid to find the highest for the prices by using a loop similar to what is used in SetValues.

                        I would suggest to experiment with the sample and use Print statements for parts that you don't understand to ensure you fully understand how the dictionaries and series work to access the data. Once you do that it will be much easier to see how to add OnMarketData if you wanted to make that change.



                        JesseNinjaTrader Customer Service

                        Comment


                          #27
                          Hello Jesse

                          Sorry I do not understand what you are trying to convey, Please provide step by step and exactly where I need to change.
                          do I need to replace provided with OnBarUpdate() or what? if replaced, it provided error that class is not declared.


                          You would need logic that is similar to the following in OnMarketData, this is just the existing OnBarUpdate accumulation which would need modified to work with the OnMarketData variables.


                          If you mean highest bid or ask volume at a price then the dictionaries can be used for that. If you mean highest bid+ask volume on a single bar the script also does that using its BuyVolume plot.If you look in the SetValues method that shows the dictionarires and how the volume accumulation is happening. The MySeriesDictAsk MySeriesDictBid are used to store the price/volume infomration per bar. You can access the values in MySeriesDictAsk/Bid to find the highest for the prices by using a loop similar to what is used in SetValues.
                          I mean highest bid+ask volume on a single bar the script also does that using its BuyVolume plot. but i do not understand what you mean by BuyVolume plot.



                          experiment with the sample and use Print statements for parts that you don't understan
                          I do not understand why you suggest to use Print statement. I want to visually confirm in chart which price is picked up.
                          (I can verify with other indicator)

                          Thanks
                          Marble
                          Last edited by Marble; 09-30-2022, 03:31 PM.

                          Comment


                            #28
                            Hello Marble,

                            I wont be able to provided exact step by step instructions, that is up to you as the programmer to work through the problem. We have covered multiple ways that you can do this task and I have previously provided very detailed instructions on what you would need to do by using those samples to do that task. The sample you provided in the zip is much closer to what you were originally asking for so that would be the best place to start working with that code to better understand how it works.

                            I mean highest bid+ask volume on a single bar the script also does that using its BuyVolume plot. but i do not understand what you mean by BuyVolume plot.

                            The BuyVolume plot is part of that sample that you had provided.

                            I do not know what you mean by using Print statement.

                            Print is how you can output text to the NinjaScript output window.

                            JesseNinjaTrader Customer Service

                            Comment


                              #29
                              Hi Jesse,

                              1) How do I get the hightest price of [1] barago highest volume price?
                              Code:
                              BuyVolume[barsAgo] = buys + sells;
                              The code cotains dictionary but how can I obtaint the hightest price for the previous bar?

                              2) How can I add the accumulate code that you provided? Add OnMarketUpdate()?

                              Code:
                              // Accumulate volume
                              if (price >= BarsArray[1].GetAsk(whatBar))
                              {
                              buys += volume;
                              if (TempAskDict.ContainsKey(price))
                              TempAskDict[price] += volume;
                              else
                              TempAskDict.Add(price, volume);
                              }
                              else if (price <= BarsArray[1].GetBid(whatBar))
                              {
                              sells += volume;
                              if (TempBidDict.ContainsKey(price))
                              TempBidDict[price] += volume;
                              else
                              TempBidDict.Add(price, volume);
                              }​
                              Marble
                              Attached Files
                              Last edited by Marble; 10-06-2022, 08:01 PM.

                              Comment


                                #30
                                Hello Marble,

                                1) How do I get the hightest price of [1] barago highest volume price?

                                I had detailed this in post 26:

                                If you mean highest bid or ask volume at a price then the dictionaries can be used for that. If you mean highest bid+ask volume on a single bar the script also does that using its BuyVolume plot.

                                BuyVolume plot is equal to the total buys plus sells volume. That won't give you the ability to see what the highest volume price. To get the highest volume price you would need to work with the dictionary. A dictionary contains multiple records where a price is used as a key to look up the volume at that price.

                                To use the dictionaries I would suggest looking at the code in the zip file sample that you provided, it has the SetValues method where the dictionaries are being used. To loop over a dictionary you can use the foreach loops shown in that method:

                                Code:
                                foreach (KeyValuePair<double, double> kvp in MySeriesDictAsk[0])
                                {
                                
                                }​
                                
                                or 
                                
                                foreach (KeyValuePair<double, double> kvp in MySeriesDictBid[0])
                                {
                                
                                }

                                2) How can I add the accumulate code that you provided? Add OnMarketUpdate()?
                                This is already a part of the sample you provided. You don't need to add anything else to that sample for it to work. Please see the zip file you uploaded called BuySellVolumeOneTickDictionaries




                                JesseNinjaTrader Customer Service

                                Comment

                                Latest Posts

                                Collapse

                                Topics Statistics Last Post
                                Started by swestendorf, Today, 11:14 AM
                                2 responses
                                5 views
                                0 likes
                                Last Post NinjaTrader_Kimberly  
                                Started by xiinteractive, 04-09-2024, 08:08 AM
                                4 responses
                                12 views
                                0 likes
                                Last Post xiinteractive  
                                Started by Mupulen, Today, 11:26 AM
                                0 responses
                                1 view
                                0 likes
                                Last Post Mupulen
                                by Mupulen
                                 
                                Started by Sparkyboy, Today, 10:57 AM
                                1 response
                                5 views
                                0 likes
                                Last Post NinjaTrader_Jesse  
                                Started by TheMarlin801, 10-13-2020, 01:40 AM
                                21 responses
                                3,917 views
                                0 likes
                                Last Post Bidder
                                by Bidder
                                 
                                Working...
                                X