But when I call it from the strategy with the following code, the output is blank/empty:
string mySlope = zSTD(128,1).mySlopeX;
Print("mySlope= " + mySlope);
Here is the updated indicator code:
//
// Copyright (C) 2023, NinjaTrader LLC <www.ninjatrader.com>.
// NinjaTrader reserves the right to modify or overwrite this NinjaScript component with each release.
//
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
using SharpDX;
using SharpDX.Direct2D1;
#endregion
//This namespace holds indicators in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Indicators
{
/// <summary>
/// Linear regression is used to calculate a best fit line for the price data. In addition an upper and lower band is added by calculating the standard deviation of prices from the regression line.
/// </summary>
public class zSTD : Indicator
{
private Series<double> interceptSeries;
private Series<double> slopeSeries;
private Series<double> stdDeviationSeries;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "zSTD";
Name = "zSTD";
IsAutoScale = false;
IsOverlay = true;
IsSuspendedWhileInactive = true;
Period = 35;
Width = 2;
AddPlot(Brushes.DarkGray, NinjaTrader.Custom.Resource.NinjaScriptIndicatorMiddle);
AddPlot(Brushes.Magenta, NinjaTrader.Custom.Resource.NinjaScriptIndicatorUpper);
AddPlot(Brushes.Magenta, NinjaTrader.Custom.Resource.NinjaScriptIndicatorLower);
}
else if (State == State.DataLoaded)
{
interceptSeries = new Series<double>(this);
slopeSeries = new Series<double>(this);
stdDeviationSeries = new Series<double>(this);
}
}
private string mySlope; // wanting to expose & pass this variable to strategy
protected override void OnBarUpdate()
{
// First we calculate the linear regression parameters
double sumX = (double) Period*(Period - 1)*.5;
double divisor = sumX*sumX - (double) Period*Period*(Period - 1)*(2*Period - 1)/6;
double sumXY = 0;
double sumY = 0;
int barCount = Math.Min(Period, CurrentBar);
for (int count = 0; count < barCount; count++)
{
sumXY += count*Input[count];
sumY += Input[count];
}
if (divisor.ApproxCompare(0) == 0 && Period == 0) return;
double slope = (Period*sumXY - sumX*sumY)/divisor;
double intercept = (sumY - slope*sumX)/Period;
slopeSeries[0] = slope;
interceptSeries[0] = intercept;
// Next we calculate the standard deviation of the residuals (vertical distances to the regression line).
double sumResiduals = 0;
for (int count = 0; count < barCount; count++)
{
double regressionValue = intercept + slope * (Period - 1 - count);
double residual = Math.Abs(Input[count] - regressionValue);
sumResiduals += residual;
}
double avgResiduals = sumResiduals / Math.Min(CurrentBar - 1, Period);
sumResiduals = 0;
for (int count = 0; count < barCount; count++)
{
double regressionValue = intercept + slope * (Period - 1 - count);
double residual = Math.Abs(Input[count] - regressionValue);
sumResiduals += (residual - avgResiduals) * (residual - avgResiduals);
}
double stdDeviation = Math.Sqrt(sumResiduals / Math.Min(CurrentBar + 1, Period));
stdDeviationSeries[0] = stdDeviation;
double middle = intercept + slope * (Period - 1);
Middle[0] = CurrentBar == 0 ? Input[0] : middle;
Upper[0] = stdDeviation.ApproxCompare(0) == 0 || Double.IsInfinity(stdDeviation) ? Input[0] : middle + stdDeviation * Width;
Lower[0] = stdDeviation.ApproxCompare(0) == 0 || Double.IsInfinity(stdDeviation) ? Input[0] : middle - stdDeviation * Width;
}
#region Properties
[Browsable(false)]
[XmlIgnore()]
public Series<double> Lower
{
get { return Values[2]; }
}
[Browsable(false)]
[XmlIgnore()]
public Series<double> Middle
{
get { return Values[0]; }
}
[Range(2, int.MaxValue), NinjaScriptProperty]
[Display(ResourceType = typeof(Custom.Resource), Name = "Period", GroupName = "NinjaScriptGeneral", Order = 0)]
public int Period
{ get; set; }
[Browsable(false)]
[XmlIgnore()]
public Series<double> Upper
{
get { return Values[1]; }
}
[Range(1, double.MaxValue), NinjaScriptProperty]
[Display(ResourceType = typeof(Custom.Resource), Name = "Width", GroupName = "NinjaScriptGeneral", Order = 1)]
public double Width
{ get; set; }
// attempting to enable passing of indicator variable to strategy
[Browsable(false)]
[XmlIgnore()]
public string mySlopeX {
get {
Update(); // ensures exposed variable is up to date
return mySlope; // the desired variable to be exposed
}
}
#endregion
#region Misc
private int GetXPos(int barsBack)
{
return ChartControl.GetXByBarIndex(ChartBars,
Math.Max(0, Bars.Count - 1 - barsBack - (Calculate == Calculate.OnBarClose ? 1 : 0)));
}
private int GetYPos(double price, ChartScale chartScale)
{
return chartScale.GetYByValue(price);
}
protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
{
if (Bars == null || ChartControl == null) return;
RenderTarget.AntialiasMode = AntialiasMode.PerPrimitive;
ChartPanel panel = chartControl.ChartPanels[ChartPanel.PanelIndex];
int idx = BarsArray[0].Count - 1 - (Calculate == Calculate.OnBarClose ? 1 : 0);
double intercept = interceptSeries.GetValueAt(idx);
double slope = slopeSeries.GetValueAt(idx);
double stdDev = stdDeviationSeries.GetValueAt(idx);
int stdDevPixels = (int) Math.Round(((stdDev*Width)/(chartScale.MaxValue - chartScale.MinValue))*panel.H, 0);
int xPos = GetXPos(Period - 1 - Displacement);
int yPos = GetYPos(intercept, chartScale);
int xPos2 = GetXPos(0 - Displacement);
int yPos2 = GetYPos(intercept + slope*(Period - 1), chartScale);
Vector2 startVector = new Vector2(xPos, yPos);
Vector2 endVector = new Vector2(xPos2, yPos2);
// Middle
RenderTarget.DrawLine(startVector, endVector, Plots[0].BrushDX, Plots[0].Width, Plots[0].StrokeStyle);
if ( startVector.Y - endVector.Y > 0 ) { mySlope = "Up"; }
if ( startVector.Y - endVector.Y < 0 ) { mySlope = "Down"; }
Draw.TextFixed( this, "myText", mySlope, TextPosition.TopRight ); // works as expected
// Upper
RenderTarget.DrawLine(new Vector2(startVector.X, startVector.Y - stdDevPixels), new Vector2(endVector.X, endVector.Y - stdDevPixels), Plots[1].BrushDX, Plots[1].Width, Plots[1].StrokeStyle);
// Lower
RenderTarget.DrawLine(new Vector2(startVector.X, startVector.Y + stdDevPixels), new Vector2(endVector.X, endVector.Y + stdDevPixels), Plots[2].BrushDX, Plots[2].Width, Plots[2].StrokeStyle);
RenderTarget.AntialiasMode = AntialiasMode.Aliased;
}
#endregion
}
}
#region NinjaScript generated code. Neither change nor remove.
namespace NinjaTrader.NinjaScript.Indicators
{
public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
{
private zSTD[] cachezSTD;
public zSTD zSTD(int period, double width)
{
return zSTD(Input, period, width);
}
public zSTD zSTD(ISeries<double> input, int period, double width)
{
if (cachezSTD != null)
for (int idx = 0; idx < cachezSTD.Length; idx++)
if (cachezSTD[idx] != null && cachezSTD[idx].Period == period && cachezSTD[idx].Width == width && cachezSTD[idx].EqualsInput(input))
return cachezSTD[idx];
return CacheIndicator<zSTD>(new zSTD(){ Period = period, Width = width }, input, ref cachezSTD);
}
}
}
namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
{
public Indicators.zSTD zSTD(int period, double width)
{
return indicator.zSTD(Input, period, width);
}
public Indicators.zSTD zSTD(ISeries<double> input , int period, double width)
{
return indicator.zSTD(input, period, width);
}
}
}
namespace NinjaTrader.NinjaScript.Strategies
{
public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
{
public Indicators.zSTD zSTD(int period, double width)
{
return indicator.zSTD(Input, period, width);
}
public Indicators.zSTD zSTD(ISeries<double> input , int period, double width)
{
return indicator.zSTD(input, period, width);
}
}
}
#endregion

Comment