OnCalculateMinMax() / OnRender (IsAutoScale)

When using SharpDX to draw objects, IsAutoScale does not work, so I gather you have to use OnCalculateMinMax() to list the min/max for the chart to scale.

My question is, is this run BEFORE OnRender?

The objects I’m drawing inside onRender are using things like Text Metrics to determine their position, but if OnRender runs after OnCalculateMinMax() then do I have to repeat these calculations inside OnCalculateMinMax() as passing values from OnRender to OnCalculateMinMax() won’t work.

You are correct that SharpDX drawings don’t figure into AutoScale calculations. You need OnCalculateMinMax() for that.

OnCalculateMinMax() is its own method that needs to get overridden inside your NinjaScript object. This is done separately and outside OnRender. If you need to pass values back and forth between the two, you’ll need to define class level variables that hold your max and min y-values. These will get calculated and assigned in OnRender and used in OnCalculateMinMax(). Just make sure the values are initialized properly and defined at all times just in case the two methods run in different threads and asynchronously - I’m not 100% sure about this part, but ensuring variables are initialized and defined can save you some trouble in this scenario.

One thing to be aware of with OnCalculateMinMax() that I learned the hard way was that I believe you always need to assign values to MinValue and MaxValue before you return from it, in other words don’t do premature returns before making the assignments.

Hope this helps.

ok, that sounds like I imagined it in my head, the only thing I couldn’t get my head around was onCalculateMinMax running first, then you do the calculations after.

1 Like

I may be wrong on this, but I don’t think you can reliably predict when OnCalculateMinMax will run compared to other methods, so it’s best to account for this in your code.

Initialize your class level variables that hold min/max values to zero.

Then in OnCalculateMinMax, you need to add some error checking logic that if these values are zero, then set the scale to some default value. For example, you can loop through visible bars on the chart and and find the highs and lows and use that to set the scale.

Then, when you go through OnRender and place your objects on the chart, you can adjust those min/max levels. Next time OnCalculateMinMax runs, it will bypass the initial check and go through your auto scale logic.

Hope this helps.

Makes sense, but why would it matter if the values are zero?

Don’t the chartBars themselves override any values that are less than the chart bars?

I mean like, why do I need to loop through the ChartBars, when the ChartBars are another object on the chart and are always autoscaled.

Again, I may be wrong on this, but my understanding is that when you use OnCalculateMinMax(), that you’re overriding any other auto scaling. I’m not sure what will happen if the min/max levels are set to zero, but I don’t want to risk having my tools act wonky on a customer’s platform. So I like to have reasonable values for min/max to avoid any potential issues.

Who knows maybe current versions don’t have any issues with min/max being zero, but a future version of NT may change something and having a zero value sends the platform into a tailspin. I like to cover my bases and write future proof code as much as I can.

ok, I guess if you set zero, it will try and make sure the price of 0 is on the chart, so that would shrink the chart to a thin line(as zero is the most extreme value on one end).

So it seems like if you don’t have any drawings within the visible bars on the chart(To/FromIndex, you need to set it to something that isn’t greater than the high of the highest high, and the low of the lowest low because that’s all you can do. This makes sense, so I guess like you say you do need to loop through the visible bars,

One other thing I read was it’s also possible to add a dummy plot with transparent color and then this will scale. But I’m not sure it’s possible to write to specific bar plot values from within onRender. thanks

I got it to work. I created 2 global variables to store the high and low from my OnRender drawings and then this inside OnCalculateMinMax. Originally I tried to avoid looping through the bars but nothing worked (HighestBar, etc), even when storing as globals.

	public override void OnCalculateMinMax()
	{
		if (ChartBars == null)
			return;

		// Reset our variables
		double barsLow  = double.MaxValue;
		double barsHigh = double.MinValue;

		// Loop only through visible bars & store High/Low
		for (int i = ChartBars.FromIndex; i <= ChartBars.ToIndex; I++)
		{
			double highValue = High.GetValueAt(i);
			double lowValue  = Low.GetValueAt(i);

			if (highValue > barsHigh) barsHigh = highValue;
			if (lowValue  < barsLow)  barsLow  = lowValue;
		}

		// Compare to global SharpDX values
		double lowestValue  = Math.Min(SharpDX_Bottom, barsLow);
		double highestValue = Math.Max(SharpDX_Top, barsHigh);

		// Set the Y-axis min/max
		MinValue = lowestValue;
		MaxValue = highestValue;
	}
1 Like

ok, I have to report it’s not working correctly.

During a live market I get the tiniest of flutter when Autoscale is on. Like a 1-2 pixel judder. I guess because onRender is called after.

1 Like

ok, I fixed it. I was using GetValueByY inside onRender, updating a global variable which is used inside OnCalculateMinMax, but it produces jitter. The solution was to RoundToTickSize on the value. And then no jitter.

2 Likes

Your OnCalculateMinMax code looks good to me - nice work. Using RoundToTickSize makes sense to avoid the jitter. In my case, the values that I use are based on price so they’re already rounded. I guess I avoided this “learning experience” LOL.

To answer your previous comment about using dummy plots with transparent color, I don’t think transparent plots figure into auto scale calculations, so that probably won’t work But if you want a hidden plot, you can always make it the same color as the chart background and make sure ZOrder is set to a large negative value so it won’t cover any other drawings.

Before learning about OnCalculateMinMax, I used a similar hack. I drew two thin horizontal lines at the desired levels, set them to auto scale and made them the same color as chart background with a large negative ZOrder. This was mostly ok, but if user changed their chart background and didn’t refresh (F5), they would see the lines.

OnCalculateMinMax is a lot cleaner and more elegant solution.

1 Like

I’m still struggling with this. I thought it was fixed but now I have this weird issue.

When the drawing which is currently the boundary of Min is moved closer to the priceScale, it starts to jitter.

I added some Prints and what’s happening is, when I move it close to the price scale, on each render cycle gives me different value for GetYByValue by 1 tick alternating.

When the drawing is away from the price scale, the value is constant.

The fact this only happens when I move it close to the price scale is very bizarre.

As you squeeze the time scale and move the drawing closer to the price scale, if you notice you’re also changing the price scale. So that is probably what’s contributing to the difference in calculation and not necessarily the fact that the drawing is getting closer to the price scale.

Print some of the intermediate variables during the calculation steps in OnCalculateMinMax and see if there are any fractional values that might be right at the edge of rounding up or down to the nearest tick. This is probably the cause for the jitter. Maybe you want to round everything up (or down as appropriate) to nearest integer to avoid this.

1 Like

I know the price scale changes but what I’m saying is. When the chart is stationary;

Render cycle 1: value
Render cycle 2: value + 1 tick
Render cycle 3: value
Render cycle 4: value + 1 tick

It’s like the chart glitches, then I grab the Y(hence the offset) and then it’s passed to CalculateMinMax.

I think this goes back to what I was saying in the beginning. It’s impossible to know the Y position of the drawing until you draw them, but if OnCalculateMinMax runs before onRender, then it’s going to create some issues.

Maybe I’m not understanding this correctly, so I apologize for that in advance.

Don’t you add the objects on the chart in the same script? If so, then you already know the Y value of the object. Store it in a class level variable and utilize it in OnCalculateMinMax. Maybe add a few ticks or a point to account for object’s size. If the object is a geometric shape with well defined dimensions, then you should be able to calculate its extremes. The sequence of running OnRender and OnCalculateMinMax shouldn’t matter.

Yea, I’m drawing in the same script ofc, and I am storing it in a class field and using in CalculateMinMax.

Essentially each render cycle I’m storing the lowest/highest part of each drawing and writing them to the class field, the most extreme values are the ones that get used inside CalculateMinMax. This seems the logical way and sounds like the same way you’d do it.

I

Sorry, I’m running out of ideas on what could be causing your jitter. It’s hard to know what’s going on without seeing the actual code. My first instance of working with OnCalculateMinMax was a total disaster :joy: and took a while and lots of diagnostics to get things working properly and without crashing. Certain things aren’t intuitively obvious and not every nuance is documented.

By any chance, do you have multiple data series in your script?

Looking back at the code you posted above, if you do have multiple data series in your script, keep in mind that the data series index may not be reliably known in the OnCalculateMinMax thread. So where you have things like High.GetValueAt(i) and Low.GetValueAt(i), I would recommend changing to BarsArray[0].GetHigh(i) and BarsArray[0].GetLow(i). High and Low could point to a different data series depending on the value of BarsInProgress and may not give you actual price bar highs and lows if you have a secondary data series with a different timeframe.

This is actually a good idea to do regardless and not rely on shortcuts like High, Low, Close, Bars, CurrentBar, etc, which could point to different series. Only place I would consider using these shortcuts is in OnBarUpdate and nowhere else. Always use the BarsArray syntax to definitively point to the correct data series.

Yea, thanks for your help. I have no added series however. I will keep trying.