Heikin-Ashi
All functions · nseries package
The candles and the oscillators
Heikin-ashi candles (Valcu) redraw each bar from the bar’s open, high, low and close and the previous heikin-ashi candle: the close is the bar’s average price, the open is the midpoint of the previous heikin-ashi body, and the high and low take in the new open and close. Vervoort’s HACO and HACOLT read the candles, zero-lag TEMAs and his printed candle conditions into a state. The six methods of this category take the opens, highs, lows and closes of one table of bars as (o, h, l, c) after an unused receiver, as GarmanKlassVolatility(o, h, l, c, n) does; callers write c.HACO(o, h, l, c, 34).
| Method | On each bar of the span |
|---|---|
HeikinAshiOpen | the open: o[t0] on the first bar, then the midpoint of the previous heikin-ashi open and close |
HeikinAshiClose | the close: (((o + h) + l) + c) / 4 |
HeikinAshiHigh | the high: Go’s max of the bar’s high, the heikin-ashi open and the heikin-ashi close |
HeikinAshiLow | the low: Go’s min of the bar’s low, the heikin-ashi open and the heikin-ashi close |
HACO | Vervoort’s latch: 1 from an up switch, 0 from a down switch, 0 before the first |
HACOLT | Vervoort’s long-term position: +1 long, 0 flat, -1 short, 0 before the first switch |
They share these rules:
- One table of bars. o, h, l and c must have equal lengths; otherwise every method returns an empty series,
Series{}, before it reads its parameters. The bars are not right-aligned (OHLC columns are one table, not two series), and the receiver is not read. - The span. t0 is the first bar at which all four inputs are finite, and the span runs from t0 to the bar before the next bar with a NaN, +Inf or -Inf in any input. Every output is NaN outside the span, after it too, even where the inputs are finite again. On the span each method computes as if the series started at t0: there is no warm-up, and the first valid index is t0.
- The seed. The open starts at the real open of t0, as Valcu’s spreadsheet does. pandas-ta and many charting ports start it at
(o[0] + c[0]) / 2; the two opens differ by(o[0] - c[0]) / 2^(t+1)at bar t, which halves each bar and rounds away once it is below half a unit in the last place: on random walks whose daily moves are about a hundredth of the price, the opens agree bit for bit from bar 46 on in the median, and from bar 60 on at the latest in 3,000 such walks. - Bars as given. A malformed bar (an open or close outside the range, or a high below the low) is computed as it is, and overflow follows IEEE arithmetic without ending the span.
- The oscillators call the package’s
TEMAon the span, whose simple-average seeds may differ from MetaStock’s (the sources do not document MetaStock’s), and their switches in the first 6(n-1) bars or so depend on the seeding and on where the slice starts. Each is a latch that remembers its last switch.
Sources. Valcu, “Using The Heikin-Ashi Technique”, Technical Analysis of Stocks & Commodities, February 2004 (the candles, haDiffCO and its average, the white and black bodies); Vervoort, “Trading With The Heikin-Ashi Candlestick Oscillator”, Technical Analysis of Stocks & Commodities, December 2008 (HACO); Vervoort, “Long-Term Trading Using Exchange Traded Funds”, Technical Analysis of Stocks & Commodities, July 2012 (HACOLT); Vervoort, “The Quest For Reliable Crossovers”, Technical Analysis of Stocks & Commodities, May 2008 (the zero-lag TEMA crossover). The recipes under “Compositions” below build Valcu’s indicators, Vervoort’s four-point close, smoothed heikin-ashi and the crossover from the views.
HACO
func (s Series) HACO(o Series, h Series, l Series, c Series, n int) Series
HACO returns Vervoort’s heikin-ashi candlestick oscillator, a latch that turns 1 when his up leg takes over from his down leg and 0 on the reverse, from zero-lag TEMAs of length n of the median price and of his four-point heikin-ashi close, with his printed candle conditions.
The receiver is not used. The inputs o, h, l and c must have equal lengths; otherwise the result is an empty series, Series{}. Empty inputs also return a non-nil empty series. Lengths are checked before parameters. If n < 1 the result is len(o) zeros, including at bars with non-finite inputs. Any positive n is valid, including lengths greater than the span. The default is 34 bars, using a single length for both legs as Vervoort recommends.
With valid parameters, let t0 be the first bar at which all four inputs are finite. Values before t0 are NaN. The first later bar with a non-finite input and every later bar are NaN, even if finite inputs resume. If there is no finite starting bar, all values are NaN. The candles, averages and latch run on the span as if the series started there. There is no warm-up: the first valid index is t0. A malformed bar is computed as it is; overflow follows IEEE arithmetic and does not end the span.
Valcu’s open is seeded with o[t0] and recursively fed by ohlc4 = (((o + h) + l) + c) / 4. Vervoort’s four-point close haCV is (((ohlc4 + haOpen) + max(h, haOpen)) + min(l, haOpen)) / 4, in that printed order. Its green test is haCV >= haOpen, including ties, and its red test is haCV < haOpen. For either haCV or the median price (h + l) / 2, set t = x.TEMA(n); its zero-lag average is t.Add(t.Sub(t.TEMA(n))). The difference d subtracts the zero-lag four-point close from the zero-lag median and is compared with zero.
Each leg starts with Alert over 2 bars: a green candle now or previously keeps the up leg, while a red candle now or previously keeps the down leg. The up leg also keeps when d >= 0, and the down leg when d < 0. The up keepall is ku OR (previous ku AND c >= o) OR c >= previous c; the down keepall is kd OR (previous kd AND c < o) OR c < previous c. This is the printed grouping: an up close or down close can keep its leg on its own. The article’s step 2 instead groups both price tests behind the previous keeping flag; that alternative is not used.
A small body satisfies abs(c - o) < (h - l) times 0.35. The up leg utr holds if its keepall holds, or if the previous up keepall holds, the body is small and h >= previous l. The down leg dtr holds if its keepall holds, or if the previous down keepall holds, the body is small and l <= previous h. Every condition that reads the previous bar is false at the first bar.
An up switch upw requires utr and a transition of dtr from true to false; a down switch dnw requires dtr and a transition of utr from true to false. The latch is upw.Sub(dnw).Hysteresis(0, 0).GT(0): it is 0 before the first switch, becomes 1 on an up switch, becomes 0 on a down switch and otherwise holds. The output is 1 or +0.
The package’s TEMA uses three XAverage stages, each passing through its first n-1 inputs and taking an n-bar simple-average seed before the exponential recursion. MetaStock’s seeding is not documented in the sources and may differ, and switches in the first 6(n-1) bars or so depend on the seeding. The latch remembers its last switch, so runs from different history starts can retain different states until they have both passed the same switch.
His crossover recipe uses zl(h.Add(l).Add(c).Div(3)).CrossesAboveS(zl(haCV)) with length 55, where zl is the zero-lag TEMA above and haCV is his four-point close. It comes from Vervoort, “The Quest For Reliable Crossovers”, Technical Analysis of Stocks & Commodities, May 2008.
The work is O(1) per bar, with a fixed number of series allocations and O(1) state for the legs and latch. Invalid parameters allocate only the result. Inputs are unchanged.
Source: Vervoort, “Trading With The Heikin-Ashi Candlestick Oscillator”, Technical Analysis of Stocks & Commodities, December 2008.
HACOLT
func (s Series) HACOLT(o Series, h Series, l Series, c Series, n int, candleSize float64, ltAverage int) Series
HACOLT returns Vervoort’s long-term heikin-ashi candlestick oscillator as +1 (long), 0 (flat) or -1 (short), for his 100, 50 and 0.
The receiver is not used. The inputs o, h, l and c must have equal lengths; otherwise the result is an empty series, Series{}. Empty inputs also return a non-nil empty series. Lengths are checked before parameters. If n < 1, ltAverage < 1, or candleSize is negative or not finite, the result is len(o) zeros, including at bars with non-finite inputs. Both +0 and -0 are valid candle sizes and make the body test false. Any positive n and ltAverage are valid, including lengths greater than the span.
The parameters follow the printed order: n is the common TEMA length in bars, default 55; candleSize is the body threshold as a fraction of the bar’s range, default 1.1; ltAverage is the long-term exponential-average length in bars, default 60. The calculation is bar-agnostic; Vervoort tested weekly bars.
With valid parameters, let t0 be the first bar at which all four inputs are finite. Values before t0 are NaN. The first later bar with a non-finite input and every later bar are NaN, even if finite inputs resume. If there is no finite starting bar, all values are NaN. The candles, averages and states run on the span as if the series started there. There is no warm-up: the first valid index is t0. A malformed bar is computed as it is; overflow follows IEEE arithmetic and does not end the span.
HACOLT uses HACO’s Valcu open, Vervoort four-point close, zero-lag TEMA difference and down leg, with candleSize in place of 0.35. The up leg additionally keeps when c >= haCV or, after the first bar, h > previous h or l > previous l. All previous-bar conditions are false at the first bar.
On well-formed bars with a positive normal range and no overflowing differences, candleSize 1.1 “has no influence” in Vervoort’s words: the body test holds, just as for larger values. Flat bars fail the strict body test at every candleSize. Smaller values can end positions sooner; malformed bars and subnormal ranges can distinguish 1.1 from larger settings.
R is the 0/1 latch formed from the modified legs and starts at 0. The long-term latch LTR starts at 1 and stays there until the first up or down switch; the output is 0 before the first switch. Thereafter R = 1 sets LTR = 1. With R = 0, LTSell is the strict comparison c < c.XAverage(ltAverage) on the span; if it holds, LTR becomes 0, otherwise LTR holds. The output is +1 when R = 1, 0 when R = 0 and LTR = 1, and -1 when R = 0 and LTR = 0, mapping his 100/50/0 to +1/0/-1.
A short opens only with R = 0 and a close below its average and persists until R returns to 1, even if the close recovers above its average. After a long closes, the output stays flat until a close below the average while R remains 0.
As with HACO, TEMA’s simple-average seeds may differ from MetaStock’s, which the sources do not document: switches in the first 6(n-1) bars or so depend on the seeding. Changing where history starts can also change the latches’ later states.
The work is O(1) per bar, with a fixed number of series allocations and O(1) state for the legs and latches. Invalid parameters allocate only the result. Inputs are unchanged.
Source: Vervoort, “Long-Term Trading Using Exchange Traded Funds”, Technical Analysis of Stocks & Commodities, July 2012.
HeikinAshiClose
func (s Series) HeikinAshiClose(o Series, h Series, l Series, c Series) Series
HeikinAshiClose returns the heikin-ashi close, the average of the bar’s open, high, low and close.
The receiver is not used. The inputs o, h, l and c must have equal lengths; otherwise the result is an empty series, Series{}. Empty inputs also return a non-nil empty series.
Let t0 be the first bar at which all four inputs are finite. Values before t0 are NaN. The first later bar with a non-finite input and every later bar are NaN, even if finite inputs resume. If there is no finite starting bar, all values are NaN. There is no warm-up: the first valid index is t0.
The value is (((o + h) + l) + c) / 4, adding in that order before dividing, and equals o.SAverage(h, l, c) on equal-length finite inputs. Its arithmetic does not depend on earlier bars, but the output shares the open’s span. A malformed bar is computed as it is. Overflow follows IEEE arithmetic and does not end the span.
It takes O(1) work per bar in one pass and allocates only the result. Inputs are unchanged.
HeikinAshiHigh
func (s Series) HeikinAshiHigh(o Series, h Series, l Series, c Series) Series
HeikinAshiHigh returns the greatest of the bar’s high, its heikin-ashi open and its heikin-ashi close.
The receiver is not used. The inputs o, h, l and c must have equal lengths; otherwise the result is an empty series, Series{}. Empty inputs also return a non-nil empty series.
Let t0 be the first bar at which all four inputs are finite. Values before t0 are NaN. The first later bar with a non-finite input and every later bar are NaN, even if finite inputs resume. If there is no finite starting bar, all values are NaN. There is no warm-up: the first valid index is t0.
The value is max(h, haOpen, haClose), using Go’s built-in max, which propagates NaN and prefers +0 over -0. A malformed bar is computed as it is. Overflow follows IEEE arithmetic and does not end the span. On well-formed bars without overflow, the close lies within the real range, so the formula reduces to max(h, haOpen). On those bars Vervoort’s four-point close is haC.SAverage(haO, haH, haL), where the names denote the four views. The high depends on the start of history through the recursive open.
It takes O(1) work per bar in one pass and allocates only the result. Inputs are unchanged.
HeikinAshiLow
func (s Series) HeikinAshiLow(o Series, h Series, l Series, c Series) Series
HeikinAshiLow returns the least of the bar’s low, its heikin-ashi open and its heikin-ashi close.
The receiver is not used. The inputs o, h, l and c must have equal lengths; otherwise the result is an empty series, Series{}. Empty inputs also return a non-nil empty series.
Let t0 be the first bar at which all four inputs are finite. Values before t0 are NaN. The first later bar with a non-finite input and every later bar are NaN, even if finite inputs resume. If there is no finite starting bar, all values are NaN. There is no warm-up: the first valid index is t0.
The value is min(l, haOpen, haClose), using Go’s built-in min, which propagates NaN and prefers -0 over +0. A malformed bar is computed as it is. Overflow follows IEEE arithmetic and does not end the span. On well-formed bars without overflow, the close lies within the real range, so the formula reduces to min(l, haOpen). The low depends on the start of history through the recursive open.
It takes O(1) work per bar in one pass and allocates only the result. Inputs are unchanged.
HeikinAshiOpen
func (s Series) HeikinAshiOpen(o Series, h Series, l Series, c Series) Series
HeikinAshiOpen returns the heikin-ashi open of each bar, the midpoint of the previous heikin-ashi body, seeded with the real open of the first finite bar (Valcu 2004).
The receiver is not used. The inputs o, h, l and c must have equal lengths; otherwise the result is an empty series, Series{}. Empty inputs also return a non-nil empty series.
Let t0 be the first bar at which all four inputs are finite. Values before t0 are NaN. The first later bar with a non-finite input and every later bar are NaN, even if finite inputs resume. If there is no finite starting bar, all values are NaN. On the span, the seed is o[t0], copied verbatim as in Valcu’s spreadsheet, and each later value is (haOpen[t-1] + haClose[t-1]) / 2. There is no warm-up: the first valid index is t0. A malformed bar is computed as it is; overflow follows IEEE arithmetic and does not end the span.
This is a recursive exponential average with alpha 1/2, lagged one bar. With t0 = 0 and non-empty finite inputs of moderate magnitude, without overflow or subnormal rounding, it equals Series{o[0]}.Append(haC[:len(o)-1]).XAverageAlpha(0.5), where haC is the close view. The alternative haC.XAverage(3).Shift(1) has different starting values because of its simple-average seed and Shift’s first zero, then agrees after those seed effects round away.
For any view X, X(o, h, l, c)[t0:] equals X(o[t0:], h[t0:], l[t0:], c[t0:]) bit for bit, with NaNs matched. The open depends on where its history starts, so moving the start of a sliding slice can change it. For a series starting at bar 0, pandas-ta instead seeds with (o[0] + c[0]) / 2. The Valcu open minus that open is (o[0] - c[0]) / 2^(t+1) at bar t in exact arithmetic. This difference halves each bar and rounds away once it is below half a unit in the last place of the open: on random walks whose daily moves are about a hundredth of the price, the two opens agree bit for bit from bar 46 on in the median, and from bar 60 on at the latest in 3,000 such walks.
Tomorrow’s open is known at today’s close. Valcu’s white body is the strict test haC.GT(haO); a tie is filled, whereas Vervoort’s green test includes a tie. Valcu’s haDiffCO is haC.Sub(haO), and Mov-haDiffCO is haC.Sub(haO).Average(3). Smoothed heikin-ashi averages the inputs first, then applies the candle views.
It takes O(1) work per bar in one pass and allocates only the result. Inputs are unchanged.
Source: Valcu, “Using The Heikin-Ashi Technique”, Technical Analysis of Stocks & Commodities, February 2004.
Compositions
Heikin-ashi recipes
The recipes below read the four views of one call each, haO, haC, haH and haL:
haO, haC := c.HeikinAshiOpen(o, h, l, c), c.HeikinAshiClose(o, h, l, c)
haH, haL := c.HeikinAshiHigh(o, h, l, c), c.HeikinAshiLow(o, h, l, c)(a) Valcu’s haDiffCO, Mov-haDiffCO and the body colour
Valcu reads the trend from the heikin-ashi body: haDiffCO is the close less the open, Mov-haDiffCO its average over p bars (his MetaStock input runs from 3 to 50, default 3), and a white body is a close strictly above the open.
diff := haC.Sub(haO)
movDiff := diff.Average(3)
white := haC.GT(haO)- Colours: Valcu’s white body is strict, so a tie (
haC == haO) is filled, not white; Vervoort’s green (HACO’s colour test, on his four-point close) includes the tie.diff.GT(0)equalswhitebit for bit. The candle kit of the “Candlesticks” category reads the four views as it reads real columns:haC.RealBody(haO)isdiff.Abs()bit for bit, the size of haDiffCO, while the kit’s colourhaC.GTE(haO)counts the tie as white, wherewhitedoes not. - First valid index: t0 for
diffandwhite; t0 + 2 formovDiff, sinceAveragepasses the input through before its first full window (bars 0 and 1 with t0 = 0) and a window that holds a NaN bar is NaN. - Anchor:
haC.QStick(haO, haC, 3)is Mov-haDiffCO in exact arithmetic, a difference of averages rather than an average of differences, so the two agree to rounding; on the real candles,c.QStick(o, c, p)is Chande’s Qstick. - Pinned by
TestRecipeHeikinAshi; runnable exampleExampleSeries_HeikinAshiClose_haDiffCO.
(b) Vervoort’s four-point close
Vervoort’s HACO and HACOLT use his own heikin-ashi close, the average of the four heikin-ashi prices, in their colour tests and zero-lag averages (the open’s recursion keeps Valcu’s close):
haCV := haC.SAverage(haO, haH, haL)- Anchor: on well-formed bars (
l <= o, c <= h) Valcu’s close lies within the bar’s range, sohaHismax(h, haO)andhaLismin(l, haO), andhaCVequals the four-point close insideHACOandHACOLT,(((ohlc4 + haO) + max(h, haO)) + min(l, haO)) / 4, bit for bit. On a malformed bar the two can differ. - First valid index: t0.
- Pinned by
TestRecipeHeikinAshi; runnable exampleExampleSeries_HeikinAshiHigh_vervoortClose.
(c) Smoothed heikin-ashi
Smoothed heikin-ashi averages the inputs first and draws the candles from the averages. Mustapha uses TradingView’s smoothed heikin-ashi with its default setting “10 and 10”, which is read here as two lengths: one for the averages of the inputs and one for a second average of the candles afterwards (his article prints no formula):
so, sh, sl, sc := o.XAverage(10), h.XAverage(10), l.XAverage(10), c.XAverage(10)
sO, sC := c.HeikinAshiOpen(so, sh, sl, sc), c.HeikinAshiClose(so, sh, sl, sc)
sO2 := sO.XAverage(10)- Anchor: with a length of 1,
XAverage(1)computesx*1 + E*0, which is the input in value (a -0 can become +0), so on finite prices other than -0 the smoothed candles are the candles bit for bit. - First valid index:
XAverage(10)passes bars 0 to 8 through and seeds with a 10-bar simple average at bar 9, so the smoothed candles start from averages at bar 9, and the second average passes nine more bars through.XAveragedoes not recover from a NaN: a missing bar k is passed through as NaN at bar k and makes every average NaN from bar max(k, 9) on, so the smoothed candles end at bar k, or at bar 9 when the missing bar is bar 0 (bars 1 to 8 are then candles of the raw inputs). Start the slice after any leading missing bars. - Pinned by
TestRecipeHeikinAshi; runnable exampleExampleSeries_HeikinAshiOpen_smoothed.
(d) The zero-lag TEMA crossover (Vervoort, May 2008)
Vervoort’s crossover buys where the zero-lag TEMA of the typical price crosses above the zero-lag TEMA of his four-point close, at length 55. The zero-lag TEMA is his printed TMA1 + (TMA1 - TMA2):
zl := func(x Series) Series {
t := x.TEMA(n)
return t.Add(t.Sub(t.TEMA(n)))
}
cross := zl(h.Add(l).Add(c).Div(3)).CrossesAboveS(zl(haCV))- Anchor:
zlis the zero-lag average insideHACOandHACOLT, bit for bit, andzl(h.Add(l).Div(2))lesszl(haCV)is their difference d on well-formed bars. - First valid index: 1 for
CrossesAboveS; the TEMAs pass their inputs through on bars 0 to n-2, and switches and crosses in the first 6(n-1) bars or so depend on the seeding, so mask them, if wanted, withcross.SetN(6*(n-1), 0).TEMAdoes not recover from a NaN either. - Pinned by
TestRecipeHeikinAshi; runnable exampleExampleSeries_HACO_crossover.