From 702d7b4a95b862350a1564c94b42e27e125c1a0b Mon Sep 17 00:00:00 2001 From: moshferatu Date: Sat, 15 Jun 2024 05:29:34 -0700 Subject: [PATCH] Initial commit of Volatility indicator --- indicators/Volatility.cs | 177 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 indicators/Volatility.cs diff --git a/indicators/Volatility.cs b/indicators/Volatility.cs new file mode 100644 index 0000000..7322c58 --- /dev/null +++ b/indicators/Volatility.cs @@ -0,0 +1,177 @@ +#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.Gui.Tools; +using NinjaTrader.Data; +using NinjaTrader.NinjaScript; +using NinjaTrader.Core.FloatingPoint; +using NinjaTrader.NinjaScript.DrawingTools; +using NinjaTrader.Gui.PropertiesTest; +#endregion + +//This namespace holds Indicators in this folder and is required. Do not change it. +namespace NinjaTrader.NinjaScript.Indicators +{ + public class Volatility : Indicator + { + private List volatilities; + private double currentVolatility; + + protected override void OnStateChange() + { + if (State == State.SetDefaults) + { + Description = @"Measures volatility based on the standard deviation of price over the defined period"; + Name = "Volatility"; + Calculate = Calculate.OnPriceChange; + IsOverlay = false; + DisplayInDataBox = true; + DrawOnPricePanel = true; + PaintPriceMarkers = true; + ScaleJustification = ScaleJustification.Right; + IsSuspendedWhileInactive = true; + + StdDevPeriod = 20; + PctRankPeriod = 252; + EMAPeriod = 100; + + AddPlot(new Stroke(Brushes.Yellow, DashStyleHelper.Solid, 3), PlotStyle.Line, "Volatility"); + AddPlot(new Stroke(Brushes.Gray, DashStyleHelper.Solid, 3), PlotStyle.Line, "EMA"); + } + else if (State == State.DataLoaded) + { + volatilities = new List(); + currentVolatility = double.MinValue; + } + } + + protected override void OnBarUpdate() + { + if (CurrentBar < StdDevPeriod) + return; + + if (IsFirstTickOfBar && currentVolatility > double.MinValue) + { + volatilities.Add(currentVolatility); + + if (volatilities.Count > PctRankPeriod) + volatilities.RemoveAt(0); + } + + currentVolatility = StdDev(Close, StdDevPeriod)[0]; + + PercentRank[0] = CalculatePercentRank(volatilities, currentVolatility); + Average[0] = EMA(Value, EMAPeriod)[0]; + } + + private double CalculatePercentRank(List values, double currentValue) + { + if (values.Count == 0) + return 0; + + int rank = values.Count(v => v <= currentValue); + return (double)rank / values.Count * 100.0; + } + + public override string DisplayName + { + get { return Name; } + } + + [Browsable(false)] + [XmlIgnore] + public Series PercentRank + { + get { return Values[0]; } + } + + [Browsable(false)] + [XmlIgnore] + public Series Average + { + get { return Values[1]; } + } + + [Range(1, int.MaxValue), NinjaScriptProperty] + [Display(Name = "Std Dev Period", Description = "Period for calculating the standard deviation of price", Order = 1, GroupName = "Volatility")] + public int StdDevPeriod { get; set; } + + [Range(1, int.MaxValue), NinjaScriptProperty] + [Display(Name = "Pct Rank Period", Description = "Period for calculating the percent rank of volatility", Order = 2, GroupName = "Volatility")] + public int PctRankPeriod { get; set; } + + [Range(1, int.MaxValue), NinjaScriptProperty] + [Display(Name = "EMA Period", Description = "Period for calculating the moving average of price", Order = 3, GroupName = "Volatility")] + public int EMAPeriod { get; set; } + } +} + +#region NinjaScript generated code. Neither change nor remove. + +namespace NinjaTrader.NinjaScript.Indicators +{ + public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase + { + private Volatility[] cacheVolatility; + public Volatility Volatility(int stdDevPeriod, int pctRankPeriod, int eMAPeriod) + { + return Volatility(Input, stdDevPeriod, pctRankPeriod, eMAPeriod); + } + + public Volatility Volatility(ISeries input, int stdDevPeriod, int pctRankPeriod, int eMAPeriod) + { + if (cacheVolatility != null) + for (int idx = 0; idx < cacheVolatility.Length; idx++) + if (cacheVolatility[idx] != null && cacheVolatility[idx].StdDevPeriod == stdDevPeriod && cacheVolatility[idx].PctRankPeriod == pctRankPeriod && cacheVolatility[idx].EMAPeriod == eMAPeriod && cacheVolatility[idx].EqualsInput(input)) + return cacheVolatility[idx]; + return CacheIndicator(new Volatility(){ StdDevPeriod = stdDevPeriod, PctRankPeriod = pctRankPeriod, EMAPeriod = eMAPeriod }, input, ref cacheVolatility); + } + } +} + +namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns +{ + public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase + { + public Indicators.Volatility Volatility(int stdDevPeriod, int pctRankPeriod, int eMAPeriod) + { + return indicator.Volatility(Input, stdDevPeriod, pctRankPeriod, eMAPeriod); + } + + public Indicators.Volatility Volatility(ISeries input , int stdDevPeriod, int pctRankPeriod, int eMAPeriod) + { + return indicator.Volatility(input, stdDevPeriod, pctRankPeriod, eMAPeriod); + } + } +} + +namespace NinjaTrader.NinjaScript.Strategies +{ + public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase + { + public Indicators.Volatility Volatility(int stdDevPeriod, int pctRankPeriod, int eMAPeriod) + { + return indicator.Volatility(Input, stdDevPeriod, pctRankPeriod, eMAPeriod); + } + + public Indicators.Volatility Volatility(ISeries input , int stdDevPeriod, int pctRankPeriod, int eMAPeriod) + { + return indicator.Volatility(input, stdDevPeriod, pctRankPeriod, eMAPeriod); + } + } +} + +#endregion