/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using QuantConnect.Data.Market; namespace QuantConnect.Indicators { /// /// This indicator computes the Heikin-Ashi bar (HA) /// The Heikin-Ashi bar is calculated using the following formulas: /// HA_Close[0] = (Open[0] + High[0] + Low[0] + Close[0]) / 4 /// HA_Open[0] = (HA_Open[1] + HA_Close[1]) / 2 /// HA_High[0] = MAX(High[0], HA_Open[0], HA_Close[0]) /// HA_Low[0] = MIN(Low[0], HA_Open[0], HA_Close[0]) /// public class HeikinAshi : BarIndicator, IIndicatorWarmUpPeriodProvider { /// /// Gets the Heikin-Ashi Open /// public IndicatorBase Open { get; } /// /// Gets the Heikin-Ashi High /// public IndicatorBase High { get; } /// /// Gets the Heikin-Ashi Low /// public IndicatorBase Low { get; } /// /// Gets the Heikin-Ashi Close /// public IndicatorBase Close { get; } /// /// Gets the Heikin-Ashi Volume /// public IndicatorBase Volume { get; } /// /// Initializes a new instance of the class using the specified name. /// /// The name of this indicator public HeikinAshi(string name) : base(name) { Open = new Identity(name + "_Open"); High = new Identity(name + "_High"); Low = new Identity(name + "_Low"); Close = new Identity(name + "_Close"); Volume = new Identity(name + "_Volume"); } /// /// Initializes a new instance of the class. /// public HeikinAshi() : this("HA") { } /// /// Gets a flag indicating when this indicator is ready and fully initialized /// public override bool IsReady => Samples > 1; /// /// Required period, in data points, for the indicator to be ready and fully initialized. /// public int WarmUpPeriod => 2; /// /// Computes the next value of this indicator from the given state /// /// The input given to the indicator /// A new value for this indicator protected override decimal ComputeNextValue(IBaseDataBar input) { if (!IsReady) { Open.Update(input.EndTime, (input.Open + input.Close) / 2); Close.Update(input.EndTime, (input.Open + input.High + input.Low + input.Close) / 4); High.Update(input.EndTime, input.High); Low.Update(input.EndTime, input.Low); } else { Open.Update(input.EndTime, (Open.Current.Value + Close.Current.Value) / 2); Close.Update(input.EndTime, (input.Open + input.High + input.Low + input.Close) / 4); High.Update(input.EndTime, Math.Max(input.High, Math.Max(Open.Current.Value, Close.Current.Value))); Low.Update(input.EndTime, Math.Min(input.Low, Math.Min(Open.Current.Value, Close.Current.Value))); } var volume = 0.0m; if (input is TradeBar) { volume = ((TradeBar)input).Volume; } else if (input is RenkoBar) { volume = ((RenkoBar)input).Volume; } Volume.Update(input.EndTime, volume); return Close.Current.Value; } /// /// Resets this indicator to its initial state /// public override void Reset() { Open.Reset(); High.Reset(); Low.Reset(); Close.Reset(); Volume.Reset(); base.Reset(); } } }