Draw area in sub chart

Questions about MultiCharts .NET and user contributed studies.
chschuster
Posts: 1
Joined: 08 May 2023

Draw area in sub chart

Postby chschuster » 08 May 2023

Hi!
I'm trying to plot bands (area) on a sub-chart. The area is computed but it is always plotted on the main chart.
Unfortunately, I have no idea how to address an individual chart (or the sub-chart)!

I have attached the code below:

using System;
using System.Drawing;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections;
using PowerLanguage.Function;
using System.Collections.Generic;

namespace PowerLanguage.Indicator
{
[SameAsSymbol(true)]
public class Bollinger_Bands_Area_MACD : IndicatorObject, IChartCustomDrawer
{
public Bollinger_Bands_Area_MACD(object _ctx)
: base(_ctx)
{
numdevsdn = -1;
numdevsup = 1;
length = 30;

fillcolor = Color.FromArgb(128, 128, 255);
transparency = 85;

macdlength = 5;
slowlength = 20;
fastlength = 4;
}

private AverageFC m_averagefc1;

private VariableSeries<Double> m_lowerband;
private VariableSeries<Double> m_upperband;

private IPlotObject Plot1;
private IPlotObject Plot2;
private IPlotObject Plot3;

private AreaValue1 m_IndicatorArea;

private bool isCSDRegistred;
private byte m_intensity; // 0-255

private ISeries<double> bollingerprice { get; set; }

private ISeries<double> testpriceuband { get; set; }

private ISeries<double> testpricelband { get; set; }

//MACD
private Function.MACD m_macd1;

private Function.XAverage m_xaverage1;

private VariableSeries<Double> m_mymacd;

private VariableSeries<Double> m_macddiff;

private IPlotObject Plot4;

private IPlotObject Plot5;

private IPlotObject Plot6;

private IPlotObject Plot7;

[Input]
public int length { get; set; }

[Input]
public double numdevsup { get; set; }

[Input]
public double numdevsdn { get; set; }

[Input]
public int displace { get; set; }

[Input]
public Color fillcolor { get; set; }

[Input]
public byte transparency
{
get
{
byte res = Convert.ToByte((float)m_intensity / 255.0 * 100.0);
return res;
}
set
{
try
{
m_intensity = Convert.ToByte(255.0 / 100.0 * (float)value);
}
catch (Exception)
{
transparency = 100;
}
}
}
[Input]
public int fastlength { get; set; }

[Input]
public int slowlength { get; set; }

[Input]
public int macdlength { get; set; }


protected override void Create()
{
m_averagefc1 = new AverageFC(this);
m_lowerband = new VariableSeries<Double>(this);
m_upperband = new VariableSeries<Double>(this);
Plot1 =
AddPlot(new PlotAttributes("UpperBand", 0, Color.Red, Color.Empty, 1, 0, true));
Plot2 =
AddPlot(new PlotAttributes("LowerBand", 0, Color.Red, Color.Empty, 1, 0, true));
Plot3 =
AddPlot(new PlotAttributes("MidLine", 0, Color.Red, Color.Empty, 1, 0, true));

m_IndicatorArea = new AreaValue1();
isCSDRegistred = false;

m_macd1 = new Function.MACD(this);
m_xaverage1 = new Function.XAverage(this);
m_mymacd = new VariableSeries<Double>(this);
m_macddiff = new VariableSeries<Double>(this);
Plot4 =
AddPlot(new PlotAttributes("MACD", 0, Color.White,
Color.Empty, 0, 0, true));
Plot5 =
AddPlot(new PlotAttributes("MACDAvg", 0, Color.Yellow,
Color.Empty, 0, 0, true));
Plot6 =
AddPlot(new PlotAttributes("MACDDiff", EPlotShapes.Histogram,
Color.Blue, Color.Empty, 0, 0,
true));
Plot7 =
AddPlot(new PlotAttributes("ZeroLine", 0, Color.Green,
Color.Empty, 0, 0, true));
}

protected override void StartCalc()
{
m_macd1.Price = Bars.Close;
m_macd1.FastLength = fastlength;
m_macd1.SlowLength = slowlength;
m_xaverage1.Price = m_mymacd;
m_xaverage1.Length = macdlength;
m_mymacd.DefaultValue = 0;
m_macddiff.DefaultValue = 0;

if (!isCSDRegistred)
{
ChartCustomDraw.Register(this);
isCSDRegistred = true;
}

//bollingerprice = Bars.Close;
bollingerprice = m_mymacd;
testpriceuband = Bars.Close;
testpricelband = Bars.Close;
m_averagefc1.price = bollingerprice;
m_averagefc1.length = length;
}

protected override void StopCalc()
{
if (isCSDRegistred)
{
ChartCustomDraw.Unregister(this);
isCSDRegistred = false;
}
}

protected override void CalcBar()
{
m_mymacd.Value = m_macd1[0];
var m_macdavg = m_xaverage1[0];
m_macddiff.Value = m_mymacd.Value - m_macdavg;

var m_avg = m_averagefc1[0];
var m_sdev = bollingerprice.StandardDeviationCustom(length, 1);
m_upperband.Value = (m_avg + (numdevsup * m_sdev));
m_lowerband.Value = (m_avg + (numdevsdn * m_sdev));
if (((displace >= 0) || Bars.CurrentBar > Math.Abs(displace)))
{
Plot1.Set(displace, m_upperband.Value);
Plot2.Set(displace, m_lowerband.Value);
Plot3.Set(displace, m_avg);

m_IndicatorArea.SetValue("UpperBand", m_upperband.Value, Bars.TimeValue.ToBinary());
m_IndicatorArea.SetValue("LowerBand", m_lowerband.Value, Bars.TimeValue.ToBinary());

Plot4.Set(0, m_mymacd.Value);
Plot5.Set(0, m_macdavg);
Plot6.Set(0, m_macddiff.Value);
Plot7.Set(0, 0);


if ((displace <= 0))
{
if (this.CrossesOver(testpricelband, m_lowerband))
{
Alerts.Alert("Price crossing over lower price band");
}
else
{
if (this.CrossesUnder(testpriceuband, m_upperband))
{
Alerts.Alert("Price crossing under upper price band");
}
}
}
}

ChartCustomDraw.ReDraw();
}


#region IChartCustomDrawer
void IChartCustomDrawer.Draw(DrawContext context, EDrawPhases phase)
{
if (phase != EDrawPhases.Final) return;

RectangleF _dr = context.FullRect;
ChartPoint DrPleft = context.Environment.Point2ChartPoint(new PointF { X = _dr.X, Y = _dr.Y });
ChartPoint DrPRight = context.Environment.Point2ChartPoint(new PointF { X = _dr.Width, Y = _dr.Height });

if (DrPleft.Time.Ticks > DrPRight.Time.Ticks)
{
return;
}

Bar[] BarsToRedraw = null;

context.Environment.Bars.Get(DrPleft.Time, DrPRight.Time, out BarsToRedraw);
if (!object.Equals(BarsToRedraw, null))
{
int countBars = BarsToRedraw.Length;
if (countBars > 0)
{
AreaPainter1 p = new AreaPainter1(fillcolor, (byte)(255 - m_intensity));
PointF[] pf = new PointF[countBars * 2];
int full = countBars * 2 - 1;
for (int i = 0, idx = 0; i < countBars; i++)
{
double high0 = m_IndicatorArea.GetValue("UpperBand", BarsToRedraw.Time.ToBinary());
double low0 = m_IndicatorArea.GetValue("LowerBand", BarsToRedraw.Time.ToBinary());
/*
if (high0 < 0|| low0 < 0)
{
idx = 0;
full-=2;
pf = new PointF[full + 1];
continue;
}
*/
Bar b = BarsToRedraw;
pf[idx] = context.Environment.ChartPoint2Point(new ChartPoint
{
Price = high0,
Time = b.Time
});

pf[full - idx] = context.Environment.ChartPoint2Point(new ChartPoint
{
Price = low0,
Time = b.Time
});
idx++;
}
p.PaintArea(context.graphics, pf);
}
}
context.DirtyRect = _dr;
}
#endregion

}

public class AreaValue1
{

ConcurrentDictionary<String, ConcurrentDictionary<long /*Datetime*/, double>> m_Data;

public AreaValue1()
{
m_Data = new ConcurrentDictionary<string,ConcurrentDictionary<long, double>>();
}

public void SetValue(String Name, double Value, long Time)
{
if(m_Data.ContainsKey(Name))
{
m_Data[Name][Time] = Value;
}
else
{
ConcurrentDictionary<long, double> data = new ConcurrentDictionary<long, double>();
while(!data.TryAdd(Time, Value))
{ }

while(!m_Data.TryAdd(Name, data))
{ }
}
}

public double GetValue(String Name, long Time)
{
try
{
if(m_Data[Name].ContainsKey(Time))
return m_Data[Name][Time];

return -1;
}
catch(System.Exception)
{
return -1;
}
}

public void Clear()
{
m_Data.Clear();
}
}

public class AreaPainter1
{
byte m_Alpha; //0->255
Color m_FillColor;

Pen m_PainterPen;
SolidBrush m_SolidBrush;
public AreaPainter1(Color color, byte Alphavalue)
{
m_FillColor = color;
m_Alpha = Alphavalue;

m_SolidBrush = new SolidBrush(Color.FromArgb(m_Alpha, m_FillColor));
m_PainterPen = new Pen(m_SolidBrush);
}

public void PaintArea(Graphics g, PointF[] p)
{
g.DrawPolygon(m_PainterPen, p);
g.FillPolygon(m_SolidBrush, p);
}

}


}

User avatar
Polly MultiCharts
Posts: 202
Joined: 20 Jul 2022
Has thanked: 1 time
Been thanked: 56 times

Re: Draw area in sub chart

Postby Polly MultiCharts » 11 May 2023

Hello chschuster,

Please see this page for detailed info on how to access additional data series.
You might also refer to this code sample for calculating the function on data2:

Code: Select all

using System; using System.Drawing; using PowerLanguage.Function; namespace PowerLanguage.Indicator { [SameAsSymbol(true)] public class Mov_Avg_Exponential_Data2 : IndicatorObject { private XAverage m_xaverage1; private VariableSeries<Double> m_avgexp; private IPlotObject Plot1; public Mov_Avg_Exponential_Data2(object ctx): base(ctx) { length = 9; } private ISeries<double> price { get; set; } [Input] public int length { get; set; } [Input] public int displace { get; set; } protected override void Create() { m_xaverage1 = new XAverage(this, 2); m_avgexp = new VariableSeries<Double>(this, 0, 2); Plot1 = AddPlot(new PlotAttributes("AvgExp", 0, Color.Blue, Color.Empty, 0, 0, true)); } protected override void StartCalc() { price = BarsOfData(2).Close;// Bars.Close; m_xaverage1.Price = price; m_xaverage1.Length = length; } protected override void CalcBar() { m_avgexp.Value = m_xaverage1[0]; if (((displace >= 0) || Bars.CurrentBar > Math.Abs(displace))) { Plot1.Set(displace, m_avgexp.Value); if ((displace <= 0)) { if (((PublicFunctions.DoubleGreater(price[0], m_avgexp.Value) && PublicFunctions.DoubleGreater(m_avgexp.Value, m_avgexp[1])) && PublicFunctions.DoubleLessEquals(m_avgexp[1], m_avgexp[2]))) { Alerts.Alert("Indicator turning up"); } else{if (((PublicFunctions.DoubleLess(price[0], m_avgexp.Value) && PublicFunctions.DoubleLess(m_avgexp.Value, m_avgexp[1])) && PublicFunctions.DoubleGreaterEquals(m_avgexp[1], m_avgexp[2]))) { Alerts.Alert("Indicator turning down"); } } } } } } }


Return to “MultiCharts .NET”