- ago
Hello everyone!
I'd like to share an indicator I've created and ask if there's a way to simplify it (aside from using a two-color histogram). Essentially, it's a colored MACD along with its companions.



CODE:
using WealthLab.Core; namespace WealthLab.Indicators { public class MACD_v2 : IndicatorBase { public override string Name => "MACD_v2"; public override string Abbreviation => "MACD_v2"; public override string HelpDescription => "MACD with signal line, colors for rise and fall, and zero line."; public override string PaneTag => "MACD_v2"; public override bool IsSmoother => false; public override PlotStyle DefaultPlotStyle { get { return PlotStyle.Histogram; } } public MACD_v2() : base() { } public MACD_v2( TimeSeries source, int shortPeriod, int longPeriod, int signalPeriod, OperatingMode operatingMode ) : base() { Parameters[0].Value = source; Parameters[1].Value = shortPeriod; Parameters[2].Value = longPeriod; Parameters[3].Value = signalPeriod; Parameters[4].Value = operatingMode; Populate(); } public static MACD_v2 Series( TimeSeries source, int shortPeriod, int longPeriod, int signalPeriod, OperatingMode operatingMode ) { string key = CacheKey("VCMMACD", shortPeriod, longPeriod, signalPeriod, operatingMode); if (source.Cache.ContainsKey(key)) return (MACD_v2)source.Cache[key]; MACD_v2 macd = new MACD_v2( source, shortPeriod, longPeriod, signalPeriod, operatingMode ); source.Cache[key] = macd; return macd; } public override void Populate() { var source = Parameters[0].AsTimeSeries; var shortPeriod = Parameters[1].AsInt; var longPeriod = Parameters[2].AsInt; var signalPeriod = Parameters[3].AsInt; var operatingMode = Enum.Parse<OperatingMode>(Parameters[4].AsString); if (shortPeriod <= 0 || longPeriod <= 0 || signalPeriod <= 0) return; if (shortPeriod > longPeriod) throw new Exception("Short period cannot be greater than long period."); DateTimes = source.DateTimes; //* Zero line if (operatingMode == OperatingMode.ZeroLine) { for (int n = 0; n < source.Count; n++) { Values[n] = 0; } return; } //* MACD var macd = EMA.Series(source, shortPeriod) - EMA.Series(source, longPeriod); if (operatingMode == OperatingMode.MACDRise) { for (int n = 1; n < source.Count; n++) { Values[n] = macd[n] >= macd[n - 1] ? macd[n] : double.NaN; if (double.IsNaN(Values[n - 1]) && !double.IsNaN(Values[n])) { Values[n - 1] = macd[n - 1]; } } return; } if (operatingMode == OperatingMode.MACDFall) { for (int n = 1; n < source.Count; n++) { Values[n] = macd[n] < macd[n - 1] ? macd[n] : double.NaN; if (double.IsNaN(Values[n - 1]) && !double.IsNaN(Values[n])) { Values[n - 1] = macd[n - 1]; } } return; } //* Signal line var signalLine = EMA.Series(macd, signalPeriod); if (operatingMode == OperatingMode.SignalLineRise) { for (int n = 1; n < source.Count; n++) { Values[n] = signalLine[n] >= signalLine[n - 1] ? signalLine[n] : double.NaN; if (double.IsNaN(Values[n - 1]) && !double.IsNaN(Values[n])) { Values[n - 1] = signalLine[n - 1]; } } return; } if (operatingMode == OperatingMode.SignalLineFall) { for (int n = 1; n < source.Count; n++) { Values[n] = signalLine[n] < signalLine[n - 1] ? signalLine[n] : double.NaN; if (double.IsNaN(Values[n - 1]) && !double.IsNaN(Values[n])) { Values[n - 1] = signalLine[n - 1]; } } return; } //* Histogram var histogram = macd - signalLine; if (operatingMode == OperatingMode.HistogramPositiveRise) { for (int n = 1; n < source.Count; n++) { Values[n] = histogram[n] >= 0 && histogram[n] >= histogram[n - 1] ? histogram[n] : double.NaN; } return; } if (operatingMode == OperatingMode.HistogramPositiveFall) { for (int n = 1; n < source.Count; n++) { Values[n] = histogram[n] >= 0 && histogram[n] < histogram[n - 1] ? histogram[n] : double.NaN; } return; } if (operatingMode == OperatingMode.HistogramNegativeRise) { for (int n = 1; n < source.Count; n++) { Values[n] = histogram[n] < 0 && histogram[n] >= histogram[n - 1] ? histogram[n] : double.NaN; } return; } if (operatingMode == OperatingMode.HistogramNegativeFall) { for (int n = 1; n < source.Count; n++) { Values[n] = histogram[n] < 0 && histogram[n] < histogram[n - 1] ? histogram[n] : double.NaN; } return; } } public override bool CalculatePartialValue() { throw new NotImplementedException(); } //generate parameters protected override void GenerateParameters() { AddParameter("Source", ParameterType.TimeSeries, PriceComponent.Close); AddParameter("Short period", ParameterType.Int32, 12); AddParameter("Long period", ParameterType.Int32, 26); AddParameter("Signal period", ParameterType.Int32, 9); AddParameter( "Operating mode", ParameterType.StringChoice, Enum.GetNames(typeof(OperatingMode))[0] ).Choices = Enum.GetNames(typeof(OperatingMode)).ToList(); } } public enum OperatingMode { HistogramPositiveRise, HistogramPositiveFall, HistogramNegativeRise, HistogramNegativeFall, MACDRise, MACDFall, SignalLineRise, SignalLineFall, ZeroLine } }


CODE:
private void PlotMACD(BarHistory bars, int shortPeriod, int longPeriod, int signalPeriod) { var macd_zeroLine = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.ZeroLine ); PlotIndicatorLine(macd_zeroLine, WLColor.White, 1, LineStyle.Solid, false, "MACD_v2"); var macd_macdRise = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.MACDRise ); PlotIndicatorLine(macd_macdRise, WLColor.Green, 2, LineStyle.Solid, true, "MACD_v2"); var macd_macdFall = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.MACDFall ); PlotIndicatorLine(macd_macdFall, WLColor.Red, 2, LineStyle.Solid, true, "MACD_v2"); var macd_signalLineRise = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.SignalLineRise ); PlotIndicatorLine( macd_signalLineRise, new WLColor(0, 128, 0), 2, LineStyle.Dashed, true, "MACD_v2" ); var macd_signalLineFall = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.SignalLineFall ); PlotIndicatorLine( macd_signalLineFall, new WLColor(128, 0, 0), 2, LineStyle.Dashed, true, "MACD_v2" ); var macd_histogramPositiveRise = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.HistogramPositiveRise ); PlotIndicator( macd_histogramPositiveRise, new WLColor(0, 100, 0), PlotStyle.Histogram, true, "MACD_v2" ); var macd_histogramPositiveFall = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.HistogramPositiveFall ); PlotIndicator( macd_histogramPositiveFall, new WLColor(0, 255, 0), PlotStyle.Histogram, true, "MACD_v2" ); var macd_histogramNegativeRise = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.HistogramNegativeRise ); PlotIndicator( macd_histogramNegativeRise, new WLColor(255, 0, 0), PlotStyle.Histogram, true, "MACD_v2" ); var macd_histogramNegativeFall = MACD_v2.Series( bars.Close, shortPeriod, longPeriod, signalPeriod, OperatingMode.HistogramNegativeFall ); PlotIndicator( macd_histogramNegativeFall, new WLColor(100, 0, 0), PlotStyle.Histogram, true, "MACD_v2" ); } }
1
104
Solved
2 Replies

Reply

Bookmark

Sort
Glitch8
 ( 12.72% )
- ago
#1
You can actually use SeriesBarColors in a custom indicator to set specific colors, here's an example of a MACD that gives each bar a random color.

CODE:
using WealthLab.Core; using System; using System.Drawing; using WealthLab.Indicators; namespace WealthLab.MyIndicators { public class MACDColor : IndicatorBase { //parameterless constructor public MACDColor() : base() { } //for code based construction public MACDColor(TimeSeries source) : base() {          Parameters[0].Value = source; Populate(); }       //static Series method       public static MACDColor Series(TimeSeries source)       {          return new MACDColor(source);       } //name public override string Name { get { return "MACDColor"; } } //abbreviation public override string Abbreviation { get { return "MACDColor"; } } //description public override string HelpDescription { get { return ""; } } //price pane public override string PaneTag { get { return "MACD"; } }       //default color       public override WLColor DefaultColor       {          get          {             return WLColor.FromArgb(255,0,0,255);          }       }       //default plot style       public override PlotStyle DefaultPlotStyle       {          get          {             return PlotStyle.Histogram;          }       } //populate public override void Populate() {          TimeSeries source = Parameters[0].AsTimeSeries; DateTimes = source.DateTimes;          SeriesBarColors = new DateSynchedList<WLColor>(source.DateTimes, WLColor.Empty);          RandomColorGenerator rg = new RandomColorGenerator();          //modify the code below to implement your own indicator calculation for (int n = 0; n < source.Count; n++)          { Values[n] = source[n];             SeriesBarColors[n] = rg.NextColor;          } } //generate parameters protected override void GenerateParameters() {          AddParameter("Source", ParameterType.TimeSeries, PriceComponent.Close); } } }
2
Best Answer
- ago
#2
Thank you very much! I was wondering how to use SeriesBarColors. 🙏🙂
0

Reply

Bookmark

Sort