Kagi

All functions · nseries package

KagiYang

func (s Series) KagiYang(reversal float64) Series

KagiYang returns the thickness state of a kagi line drawn on the receiver with a reversal amount in the receiver’s units: +1 for a yang line, from a rising line that passes the latest shoulder, -1 for a yin line, from a falling line that passes the latest waist, held between such breaks, and 0 before the first line.

The line is the machine of DirectionalChangePoints(reversal), followed step for step, with reversal as both the up and the down threshold. The first valid bar sets a running high LH and a running low LL to its value and draws no line; until the first line both extremes are tracked, and after it each line tracks only its own extreme, LH while rising and LL while falling. On each later valid bar, with p its value, the machine extends first, setting LH = p when the line is not falling and p >= LH, and LL = p when it is not rising and p <= LL, a tie moving the extreme to the latest bar. It then runs the down test (LH - p) > reversal, which turns a rising line down or draws a first falling line, and otherwise the up test (p - LL) > reversal, which turns a falling line up or draws a first rising line; a rising line runs only the down test and a falling line only the up test. Each test is one subtraction compared with the reversal, not a level such as LH - reversal, which can round differently. On a down turn the top LH that the line turned from is posted as the latest shoulder S and LL restarts at p; on an up turn the bottom LL is posted as the latest waist W and LH restarts at p. So the line’s direction is DirectionalChangePoints(reversal) on every bar, and each shoulder or waist is the extreme that DirectionalChangePoints confirms on that bar.

The first line sets the state to its direction: a first rising line is yang and a first falling line is yin. After each valid bar’s update, and on a turning bar after the turn, the state becomes +1 when the line is rising, a shoulder has been posted and p > S, becomes -1 when the line is falling, a waist has been posted and p < W, and is held otherwise. So a falling line stays yang until it passes below the latest waist, and a rising line stays yin until it passes above the latest shoulder; a turn whose own bar is already beyond the latest waist or shoulder changes the state on that bar. Both comparisons are strict: a rising line that only reaches the shoulder, or a falling line that only reaches the waist, keeps its thickness. The latch compares p, the bar’s value, rather than the line’s extreme (LH while rising, LL while falling), and the two give the same state: the extreme changes only on a bar whose value it takes, and S and W do not change within one line, so the extreme passes S or W on exactly the bar where p first does. Each comparison is with the latest shoulder or waist, not the highest shoulder, the lowest waist or the first.

The reversal is crossed only when it is exceeded: a move equal to the reversal does not turn the line, so [100, 101] at reversal 1 draws no line. For a source whose rule turns on a move equal to the amount, call the method with math.Nextafter(reversal, math.Inf(-1)), the next float64 below a positive amount. Gopalakrishnan’s worked example applies her inclusive rule with a reversal of 3 to the closes 52, 55, 53, 50, 49, 55, 54, 58, 53, 54, 50, 51, 45, 48, 47, 40, 41, 45, 46 and 50 (sessions 1 to 20), and KagiYang(math.Nextafter(3, math.Inf(-1))) reproduces her Figure 3: 0 on session 1, yang on sessions 2 and 3, yin on sessions 4 to 7, yang on sessions 8 to 12, yin on sessions 13 to 19 and yang on session 20, with shoulders 55, 58 and 48 (posted on sessions 4, 9 and 16) and waists 52, 49, 45 and 40 (posted on sessions 2, 6, 14 and 18). On session 4 the turn down from 55 to 50 already passes below the waist 52 and makes the line yin on its own bar. Her text places the change from yin to yang in session 6, but session 6 closes at 55, equal to the shoulder 55, and her own rule asks the line to extend beyond 55; her Figure 3 thickens the line only above 55, which it reaches on session 8 (58), and the strict latch follows the figure. The strict call KagiYang(3), the house rule, gives 0, 0, 0, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 by session: it draws no line on session 2 (a move of exactly 3), turns down on session 4 from the shoulder 55, and does not turn up on session 14 (45 to 48 is exactly 3), so it posts no shoulder at 48, its shoulders are 55 and 58, and the rise to 50 on session 20 stays yin.

The reversal is in receiver units, as in DirectionalChangePoints: points on prices, log units on a log series. The unit follows the method family, points here and a fraction in DirectionalChange. Gopalakrishnan prefers a percentage reversal: the line of a percentage kagi is DirectionalChange(p/100), and a thickness state on a fraction reversal is not built.

The reversal is valid when 0 <= reversal < +Inf. NaN (any payload) fails every comparison and is not valid; +Inf, -Inf and every negative value are not valid either, while +0, -0 (which compares equal to 0) and every positive finite value are valid. A reversal of 0 is not degenerate: any move against the line turns it, and a constant series draws no line. An invalid reversal gives len(s) zeros, and an empty or nil receiver gives an empty, non-nil result whatever the reversal.

There is no warm-up: the first valid index is 0 and the minimum length is 1. Bar 0 reads 0, as no line can start on the first valid bar. The 0 before the first line is the undetermined state of a data-dependent start, as in DirectionalChangePoints, not a warm-up: it is not reached again once a line is drawn, and c.DirectionalChangePoints(reversal).EQ(0) detects exactly the undetermined bars. The state is +1, -1 or +0 on every bar and is not NaN.

A bar whose value is NaN (any payload), +Inf or -Inf is missing: it updates nothing (no extension, no turn and no latch) and repeats the previous bar’s output, 0 at bar 0 and before the first line. This is the hold class of DirectionalChangePoints, where +Inf and -Inf are missing, not ordered values. Deleting every missing bar gives the same output on the remaining bars, bit for bit, and a leading run of missing bars reads 0 until the first valid bar starts the machine. Zero, -0 and negative values are ordinary values. A difference of two finite values of opposite signs near math.MaxFloat64 can overflow to +Inf, which exceeds every valid reversal (all are below +Inf) and turns the line.

The start departs from Gopalakrishnan’s. She draws the first line from the first close, the base, once a close is at least the reversal away from it, and makes the base the first waist of a first line up or the first shoulder of a first line down. This method starts from the running extremes instead, so that its line is that of DirectionalChangePoints on every bar; the first line’s waist or shoulder is then the running low or high it turned from. The two starts differ only when price wanders within the reversal of the base and then leaves on the other side: on 10, 11.5, 9, 7.5, 10.5 at reversal 2 this method gives 0, 0, -1, -1, -1 (the line turns down from 11.5 at bar 2, a shoulder that 10.5 does not pass), where the base start gives 0, 0, 0, -1, +1 (a first line down from the base 10 at bar 3, whose shoulder 10 the rise to 10.5 passes). On her worked example the two starts agree at her inclusive reversal; at the strict 3 a base start draws its first line on session 8, while this method turns down on session 4.

The state is a latch that remembers its line, shoulder and waist, so it is not start-invariant: a run started later can differ from the full run until it re-synchronises. The method is causal and deterministic: the output at bar t reads only the receiver up to bar t, every prefix of the receiver gives the first values of the full result bit for bit, nothing is revised, and repeated calls give the same bits.

The line’s direction is c.DirectionalChangePoints(reversal), +1 rising, -1 falling and 0 before the first line, and its turns are c.DirectionalChangePoints(reversal).Delta().Clamp(-1, 1), +1 on an up turn and -1 on a down turn, the first line’s included. The latest shoulder and waist are recipes: the extreme each turn confirms is read back verbatim as c.ShiftVariable(c.DirectionalChangePointsAge(reversal)), and ValueWhen holds it from the down turns as the latest shoulder and from the up turns as the latest waist, NaN before the first of each kind. The state is 0 on exactly the bars where DirectionalChangePoints(reversal).EQ(0) is 1, and it changes only to the line’s direction. Not built: a separate direction method (the direction is DirectionalChangePoints), shoulder and waist views (the recipes above), a fraction reversal, a reversal that changes from bar to bar (whose line DirectionalChangePointsScaled draws without the latch), a kagi on highs and lows, and Gopalakrishnan’s multi-level breaks and patterns, which her article does not specify precisely enough to build.

Sources: Gopalakrishnan, “Kagi Charts”, Technical Analysis of Stocks & Commodities, April 2000, after Nison, Beyond Candlesticks (1994), drawing the line on closes only, as this method does; and StockCharts’ ChartSchool, “Kagi Charts”, which describes the same breaks with an inclusive reversal. The evidence is a worked example and annotated charts with no test, and no trading edge is demonstrated.

Cost: one pass, O(1) per bar. The method allocates only the result, does not modify the receiver and is safe for concurrent use.

Verification: the method agrees bit for bit with two independent reference implementations. Negating the receiver negates the state, and on whole-number or quarter-step data, where every difference is exact, adding a whole constant or scaling the receiver and the reversal by the same power of two leaves it unchanged. On whole-number closes c below 2^52 in magnitude and a whole r >= 1, c.KagiYang(r - 0.5) equals c.PointAndFigureSignal(c, 1, r) on every bar where that signal is not 0, since the signal is the same latch on the same shoulders and waists but waits for a completed column of each kind, and c.PointAndFigureDirection(c, 1, r) equals c.DirectionalChangePoints(r - 0.5) on every bar. The bound is the engine’s: it treats a close 2^52 boxes or more from 0 as missing, while this method reads every finite close.

Recipes built on KagiYang are listed under “Compositions” in the “Kagi” category.

Compositions

Kagi recipes

KagiYang returns the thickness state of a kagi line: +1 yang, -1 yin, 0 before the first line. The line itself, its turns, its shoulders and waists and its breaks are recipes over the DirectionalChangePoints methods, whose machine KagiYang follows step for step with one reversal amount as both thresholds, and a percentage line is DirectionalChange, the fraction family of the same machine. c is the closes and R the reversal amount in the receiver’s units. Directions, turns, states, breaks and ages are whole numbers and the shoulders and waists are closes copied verbatim, so every identity marked (E) below holds bit for bit. W1 is Gopalakrishnan’s example (Figure 1 of her article, sessions 1 to 20): 52, 55, 53, 50, 49, 55, 54, 58, 53, 54, 50, 51, 45, 48, 47, 40, 41, 45, 46 and 50.

(a) The line and the inclusive source

R := math.Nextafter(3, math.Inf(-1)) // an inclusive source; plain 3 is the strict house rule
dir := c.DirectionalChangePoints(R)  // +1 while the line rises, -1 while it falls, 0 before the first line
turns := dir.Delta().Clamp(-1, 1)    // +1 on an up turn, -1 on a down turn, the first line included
yang := c.KagiYang(R)                // +1 yang, -1 yin, 0 before the first line
  • First valid index: 0. dir, turns and yang are 0 on bar 0 and until the first line, and dir.EQ(0) detects those bars: yang.EQ(0) equals dir.EQ(0) on every input (E).
  • Tie rule: a reversal is crossed only when it is exceeded. Gopalakrishnan’s rule turns the line on a move equal to the amount, so her reversal of 3 is math.Nextafter(3, math.Inf(-1)); a plain 3 is the strict house rule.
  • Anchors: on W1 at the inclusive 3, dir is [0, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1] and yang is [0, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1], her Figure 3 by session. At the strict 3, dir is [0, 0, 0, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1] and yang is [0, 0, 0, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1]: no line on session 2 (a move of exactly 3) and no turn on session 14 (45 to 48), so the rise to 50 on session 20 stays yin. turns is +1 or -1 exactly on the bars where dir changes, and on every bar where yang changes it changes to dir (E).
  • Pinned by TestRecipeKagi; runnable example ExampleSeries_KagiYang.

(b) The latest shoulder and waist

ref := c.ShiftVariable(c.DirectionalChangePointsAge(R)) // the extreme each turn confirms, copied verbatim
shoulder := ref.ValueWhen(turns.EQ(-1))                  // the top the latest falling line turned from
waist := ref.ValueWhen(turns.EQ(1))                      // the bottom the latest rising line turned from
  • First valid index: 0. shoulder is NaN until the first down turn and waist until the first up turn.
  • Anchors: shoulder and waist are the latest shoulder and waist that KagiYang compares the closes with, on every input, missing bars included (E). On W1 at the inclusive 3 the shoulders are 55, 58 and 48 (posted on sessions 4, 9 and 16) and the waists 52, 49, 45 and 40 (sessions 2, 6, 14 and 18); at the strict 3 the shoulders are 55 and 58 (sessions 4 and 9) and the waists 49 and 40 (sessions 6 and 18).
  • Not the extension: c.Sub(c.DirectionalChangePointsExtension(R)) is guaranteed to read the same extremes only where both of its subtractions are exact: each close within a factor of two of the extreme it is measured from (Sterbenz’s lemma), or prices on a grid of quarter steps. Elsewhere it can miss by an ulp: on [0, 0.1, -0.4, 0.1] at 0.01 it reads the shoulder 0.1 as 0.09999999999999998, and the composition (c) built on it makes the last bar yang, where the close only touches the shoulder and KagiYang gives -1.
  • Pinned by TestRecipeKagi; runnable example ExampleSeries_KagiYang_shoulderWaist.

(c) The state as a composition

first := turns.Mul(turns.Abs().CumSum().EQ(1)) // the first line's turn, and 0 elsewhere
state := dir.EQ(1).Mul(c.GT(shoulder)).Sub(dir.EQ(-1).Mul(c.LT(waist))).Add(first).Hysteresis(0, 0)
  • First valid index: 0, as for KagiYang.
  • Anchors: state equals c.KagiYang(R) on every input without +Inf or -Inf, NaN bars included (E): the first line sets the state to its direction, a rising line’s close above the latest shoulder makes it +1, a falling line’s close below the latest waist makes it -1, and Hysteresis(0, 0) holds it otherwise. On an infinite bar the two differ, because GT and LT read an infinity as an ordered value where KagiYang holds a missing bar: on [10, 13, 11, 9, 11, +Inf] at 1 the composition reads +1 on the last bar and KagiYang holds -1. The composition is a check that KagiYang is the latch it says, not a replacement: it allocates a Series at every step.
  • Pinned by TestRecipeKagi.

(d) Breaks and ages

breaks := yang.Delta()                   // +2 yin to yang, -2 yang to yin, +1 or -1 at the first line
buy, sell := breaks.EQ(2), breaks.EQ(-2) // "Buy on the yang, Sell on the yin"
since := breaks.BarsSince()              // bars since the state last changed
age := c.DirectionalChangePointsAge(R)   // bars since the top or bottom the current line turned from
  • First valid index: 0. breaks, buy, sell and age are 0 before the first line, and since is NaN.
  • Anchors: breaks is +1 or -1 only on the first line’s bar and +2 or -2 only on a bar where the state changes after it (E). From the first line on, c[t-age[t]] is the latest shoulder on a falling line’s bar t and the latest waist on a rising line’s (E). On W1 at the inclusive 3, breaks is [0, 1, 0, -2, 0, 0, 0, 2, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 2]: the buys of Gopalakrishnan’s rule “Buy on the yang, Sell on the yin” fall on sessions 8 and 20 and its sells on sessions 4 and 13.
  • Pinned by TestRecipeKagi; runnable example ExampleSeries_KagiYang.

(e) A percentage reversal

Gopalakrishnan prefers a reversal amount in percent. The line of a percentage kagi with a reversal of q percent is c.DirectionalChange(q/100): the same machine with ratio tests, whose thresholds are fractions (the units ruling of GLE-254). A constant expression such as 0.7/100 is folded exactly to the float64 nearest 0.007, while a run-time q/100 rounds q first: q = 0.7 gives 0.006999999999999999 (see DirectionalChange’s doc). No thickness state on a fraction reversal is built: KagiYang takes the reversal in receiver units only.

  • Pinned by TestRecipeKagi.