Point and Figure

All functions · nseries package

The chart and its six views

A point-and-figure chart records price moves of at least one box as columns of Xs (rising) and Os (falling) on a grid of boxes; time enters only through the order of the bars, and a column changes only when the price reaches a new box or reverses by reversal boxes. The methods of this category draw the chart bar by bar from highs s and lows low with the high-low method, on an absolute grid of box in the receiver’s units, and return six views of it. Each view is one pass of the same machine, with the same arguments (low, box, reversal):

MethodOn each barBefore the first column
PointAndFigureDirection+1 in a column of Xs, -1 in a column of Os0
PointAndFigureExtremethe level price of the current column’s top (Xs) or bottom (Os)NaN
PointAndFigureBoxesthe boxes the bar marks: +n Xs, -n Os, 0 when none0
PointAndFigureSignal+1 from a double-top breakout, -1 from a double-bottom breakout, held0
PointAndFigureResistancethe top of the most recently completed column of XsNaN
PointAndFigureSupportthe bottom of the most recently completed column of OsNaN

PointAndFigureDirection’s doc carries the whole machine; the others summarise it. In short:

  • The grid is anchored at 0: level k has the price kbox, the float64 product rounded once. A high is read at its X level, the largest k with kbox <= high, and a low at its O level, the smallest k with k*box >= low; a price on a level is on it. The levels come from the products, not from floor(x/box), whose float64 quotient misreads in both directions (at box 0.1 it reads 4.3 one level low and 1.7 one level high).
  • The high-low rule (StockCharts’ ChartSchool; Mike Moody in Edwards, Magee and Bassetti, Technical Analysis of Stock Trends, 11th edition, 2019, Appendix C): a column extends first, else reverses. In a column of Xs a higher X level of the high extends the column and the low is ignored; otherwise an O level of the low at least reversal boxes below the top completes the column and starts one of Os. Columns of Os mirror this. A bar does not both extend and reverse, makes at most one reversal and can mark several boxes.
  • The first column waits until the range of the usable bars spans at least reversal boxes and exactly one end of the range moved on the bar; an outside bar, which moves both, waits. The range before the first column is not a column, so the first double-top or double-bottom signal needs at least three columns.
  • Missing bars: a bar whose high or low is NaN, +Inf, -Inf or at least 2^52 boxes from 0 changes nothing (the hold class); a low above its high is used as given.

Units and boxes. The box is a fixed scalar in the receiver’s units. Close-only charts, logarithmic boxes, a grid anchored at a price, a box frozen from the ATR and the renko chart (the close-only chart with a reversal of 2 boxes) are recipes over these methods, listed under “Compositions” below. Not built: Brown’s touch rule (a high that only touches the top of a column of Xs keeps the column, even when the low would reverse it), a box re-read at each new box, the traditional box-size tiers, the triple top and the other multi-column patterns, count targets and the bullish percent of a group.

Related machines. DirectionalChangePoints is the same latch without a grid, reversing on an exact move of reversal from the column’s extreme rather than on whole boxes: on whole-number closes c below 2^52 in magnitude and a whole r >= 1, c.PointAndFigureDirection(c, 1, r) equals c.DirectionalChangePoints(r - 0.5) on every bar. The kagi line is that machine on closes, and its thickness state KagiYang (the “Kagi” category) is the same breakout latch as PointAndFigureSignal on the same shoulders and waists, without waiting for a completed column: c.KagiYang(r - 0.5) equals c.PointAndFigureSignal(c, 1, r) on every bar where the signal is not 0. The double top of PointAndFigureSignal is Vomund’s point-and-figure pattern, not the bar-chart DoubleTop of the “Chart Patterns” category, two confirmed swing highs within a band.

Sources. StockCharts, “Introduction to Point & Figure Charts”, ChartSchool (the construction example that ExampleSeries_PointAndFigureDirection replays); Moody, as above; Luis Ballesca Loyo, “Logarithmic point and figure charts”, Technical Analysis of Stocks & Commodities, January 1989 (logarithmic boxes); William G. S. Brown, “Logarithmic Point & Figure Charting”, Technical Analysis of Stocks & Commodities, July 1995 (the touch rule and the WSAU chart); David Vomund, “Classic Point And Figure”, Technical Analysis of Stocks & Commodities, December 2000 (the double-top and double-bottom signals and the box-size tiers).

PointAndFigureBoxes

func (s Series) PointAndFigureBoxes(low Series, box float64, reversal int) Series

PointAndFigureBoxes returns the boxes marked on each bar, +n for n Xs, -n for n Os and 0 when none, a reversal bar carrying the whole new column.

PointAndFigureDirection defines the grid anchored at 0 with level prices k*box rounded once in the receiver’s units and the rule for highs s and lows low: a column extends first, else reverses, and the first column waits for the reversal span and exactly one range extreme to move after the first usable bar.

An extension marks the difference between the new and previous extreme in whole levels, positive for Xs and negative for Os. A reversal marks all boxes from one beyond the completed extreme through the new extreme, so its magnitude is at least reversal. On the first column’s first bar, hiK and loK are the highest X level of the highs and lowest O level of the lows in the range so far, including that bar; the result is hiK - loK for Xs or its negative for Os. It is +0 on all other bars, including missing bars and every bar before the first column.

Let dir, e and b denote PointAndFigureDirection, PointAndFigureExtreme and PointAndFigureBoxes. After the first column’s first bar, b equals e.Div(box).Round().Delta() while levels have magnitude below 2^51. The first column’s first bar includes its whole hiK - loK span. The column height is b.Abs().SumSince(dir.Delta()), at least reversal on every bar of a column, including its first bar.

With c as closes, close mode is c.PointAndFigureBoxes(c, box, reversal).

Valid parameters satisfy 0 < box < +Inf and reversal >= 1; invalid parameters return len(s) zeros, all +0. Subnormal boxes and the largest finite box are valid. A reversal above 2^53 is valid but cannot start a column. Unequal lengths, checked first, return a non-nil empty Series. Equal empty inputs return a non-nil empty Series regardless of parameters.

A bar is missing if either price x fails math.Abs(x)/box < 2^52, including NaN, +Inf and -Inf. This is the hold class: state is unchanged and the bar marks +0 boxes. Deleting missing bars preserves the values on remaining bars bit for bit; a low above its high is used as given.

There is no warm-up: the first valid index is 0. The data-dependent start leaves +0 before the first column, and bar 0 is always undetermined, detected by dir.EQ(0). The calculation is causal and not start-invariant: a later start may choose other columns; after active runs share a direction and extreme, their boxes agree from the next bar. Prefixes and repeated calls agree bit for bit, and later bars do not revise earlier values.

The calculation is one pass, O(1) per bar regardless of the number of boxes, allocates only the result and does not change either input, including when both are the same slice.

Recipes built on the PointAndFigure methods are listed under “Compositions” in the “Point and Figure” category.

PointAndFigureDirection

func (s Series) PointAndFigureDirection(low Series, box float64, reversal int) Series

PointAndFigureDirection returns the point-and-figure column state of the highs s and lows low on an absolute grid of box in the receiver’s units with a reversal of reversal boxes: +1 in a column of Xs, -1 in a column of Os and 0 before the first column.

The grid is anchored at 0. Level k has the price kbox, with the float64 product rounded once, and level 0 has price +0. For a usable price x, its X level is the largest k with kbox <= x and its O level is the smallest k with k*box >= x; these comparisons use the rounded products. A price on a level reaches that level. Membership is set by products, not by floor(x/box): at box 0.1, 4.3 is level 43 because the rounded product of 43 and 0.1 is 4.3, but the float64 quotient is 42.99999999999999. A quotient can also over-read: 1.7 divided by 0.1 rounds to 17, while the rounded price of level 17 is above 1.7, so its X level is 16. Levels are exact whole numbers. Reaching exactly reversal boxes reverses, with no threshold tie because this is grid membership.

The high-low rule follows StockCharts, “Introduction to Point & Figure Charts”, ChartSchool, and Mike Moody in Edwards, Magee and Bassetti, Technical Analysis of Stock Trends, 11th edition, 2019, Appendix C. Bars are read from oldest to newest. A column extends first, else reverses. In Xs, a high’s X level above the extreme extends it and the low is ignored; otherwise the low’s O level at least reversal boxes below the extreme completes the X column and starts Os. In Os, a low’s O level below the extreme extends it and the high is ignored; otherwise the high’s X level at least reversal boxes above the extreme completes the O column and starts Xs. A bar does not both extend and reverse, and makes at most one reversal, although it can mark several boxes.

Before the first column, hiK is the highest X level of the highs and loK the lowest O level of the lows seen on usable bars. The first usable bar records both and waits. Later, update each extreme if it moves outwards; start only when hiK - loK >= reversal and exactly one moved. A higher hiK starts Xs at hiK; a lower loK starts Os at loK. If both moved, as on an outside bar, or neither moved, wait. The first column marks hiK - loK boxes, from one box beyond the opposite extreme through its own extreme, with positive Xs and negative Os; the range before the first column is not a column and supplies neither resistance nor support.

Each reversal records the completed X top as PointAndFigureResistance or the completed O bottom as PointAndFigureSupport, with NaN until that kind completes. After each usable update, PointAndFigureSignal becomes +1 when Xs exceed the known completed X top, or -1 when Os fall below the known completed O bottom; otherwise it holds, starting at +0. These comparisons are strict comparisons of levels, so touching a former extreme is not a breakout and the first signal needs at least three columns. PointAndFigureExtreme gives the current level price, NaN before the first column, and PointAndFigureBoxes gives the signed marks, +0 when no boxes are marked. Known level prices are finite and level 0 is +0.

The parameters require 0 < box < +Inf and reversal >= 1; otherwise every view returns len(s) zeros, all +0. Subnormal boxes and the largest finite box are valid, as is a one-box reversal; 3 is the usual reversal. A reversal above 2^53 is valid but unreachable, so the first column does not start. Unequal lengths, checked first, return a non-nil empty Series. Equal empty inputs also return a non-nil empty Series, regardless of parameters.

A price x is usable only when math.Abs(x)/box < 2^52; NaN, +Inf, -Inf and prices at or beyond that bound are missing. If either high or low is missing, the whole bar is in the hold class: it changes no state, all state views repeat their prior value and PointAndFigureBoxes is +0. Deleting missing bars preserves the outputs on the remaining bars bit for bit. No ordering check is made: a low above its high is used as given. Negative prices, +0 and -0 are ordinary values within the usable bound.

There is no warm-up: the first valid index is 0. The data-dependent undetermined state is detected by dir.EQ(0), where dir is PointAndFigureDirection; bar 0 is always undetermined and the first column needs at least two usable bars. After a column starts, its direction stays +1 or -1. The machine is causal and deterministic: prefixes and repeated calls agree bit for bit, and later bars do not revise earlier values. It is not start-invariant. Starting later keeps the same grid but can choose different columns. Once active runs share a direction and extreme, those states agree thereafter and their boxes agree from the next bar. Resistance agrees after the next X column completes, Support after the next O column completes, and Signal from the first later non-zero breakout input once those inputs agree.

Here h and l are highs and lows and c is closes. Close mode is c.PointAndFigureDirection(c, box, reversal). A logarithmic box with fraction p uses h.Log().PointAndFigureDirection(l.Log(), math.Log1p(p), reversal); level views then contain log prices and .Exp() maps them back to prices. Non-positive prices have missing logs. Ballesca Loyo, “Logarithmic point and figure charts”, Technical Analysis of Stocks & Commodities, January 1989, uses a log10 box of 0.01, which is p = 10^0.01 - 1, 2.33% per box and 7.15% per three-box reversal, and a box of 0.023026 in the natural-log units of the recipe; his log10 box of 0.005, p = 10^0.005 - 1, is 1.16% per box and 3.51% per three-box reversal.

A volatility box is a frozen scalar: choose box := c.ATRWilder(h, l, c, 14)[k], then run on h[k:] and l[k:]. Earlier bars would otherwise use a box computed from later bars. Traditional tiers use 0.50 from 5 to 20 and 1 from 20 to 100; Vomund doubles the recommended sizes, to 2 from 20 to 100 and 8 from 100 to 500, in “Classic Point And Figure”, Technical Analysis of Stocks & Commodities, December 2000. Tier selection and a box re-read at each new box are not built; the box stays fixed during a call.

Brown’s touch rule is not built. Brown, “Logarithmic Point & Figure Charting”, Technical Analysis of Stocks & Commodities, July 1995, holds a column when its extreme is touched even without another box. Here only an extension wins over a reversal: in StockCharts’ final bar the high 15.90 touches 15 but does not reach 16, so the low 12.00 reverses a three-box chart. On Brown’s WSAU replay this rule reverses to Xs in May and Os in June where his Os hold and then extend; the rules agree from July on.

DirectionalChangePoints is the corresponding machine without a grid. On closes with logarithmic fraction p, the direction is close to c.DirectionalChange((1+p)^reversal - 1), but quantisation to whole boxes and measuring the relative threshold from an exact extreme can make them differ.

With dir as the direction, dir.Delta().Clamp(-1, 1) marks +1 at X starts and -1 at O starts, including the first column. The direction changes dir.Delta().EQ(2) and dir.Delta().EQ(-2) mark X and O reversals. Any non-zero, non-NaN direction change is an event; dir.Delta().Abs().GT(0).CumSum() counts columns from the slice’s start, and dir.StateAge().Mul(dir.Abs()) counts bars in the column from 1, with 0 before the first column. Missing bars count towards that age because the state holds.

Each view makes one pass with O(1) per bar regardless of the boxes marked, allocates only the result and leaves both inputs unchanged, including aliased close inputs. Results have independent storage and concurrent calls on shared inputs are safe. Verification covers the cited constructions and independent references, with mirror and power-of-two scale identities checked bit for bit.

Recipes built on the PointAndFigure methods are listed under “Compositions” in the “Point and Figure” category.

PointAndFigureExtreme

func (s Series) PointAndFigureExtreme(low Series, box float64, reversal int) Series

PointAndFigureExtreme returns the grid level of the current column’s extreme, the top of a column of Xs or the bottom of a column of Os, in the receiver’s units, and NaN before the first column.

PointAndFigureDirection defines the grid anchored at 0 with level prices k*box rounded once in the receiver’s units and the rule for highs s and lows low: a column extends first, else reverses, and the first column waits for the reversal span and exactly one range extreme to move after the first usable bar.

At each bar in Xs the result is the top’s level price and in Os it is the bottom’s level price, with level 0 as +0. The result is NaN before the first column and only there for valid parameters; after a column starts it is finite on every bar. On data without missing bars, Xs use the X level of h.HighestSince(dir.Delta()), at most that highest high, and Os use the O level of l.LowestSince(dir.Delta()), at least that lowest low, where h and l are highs and lows and dir is PointAndFigureDirection.

With c as closes, close mode is c.PointAndFigureExtreme(c, box, reversal). For a logarithmic box of fraction p, h.Log().PointAndFigureExtreme(l.Log(), math.Log1p(p), reversal).Exp() maps level k to (1+p)^k up to rounding. The level itself is in log units; rounding of the base is magnified by the magnitude of k.

Valid parameters satisfy 0 < box < +Inf and reversal >= 1; invalid parameters return len(s) zeros, all +0. Subnormal boxes and the largest finite box are valid. A reversal above 2^53 is valid but cannot start a column. Unequal lengths, checked first, return a non-nil empty Series. Equal empty inputs return a non-nil empty Series regardless of parameters.

A bar is missing if either price x fails math.Abs(x)/box < 2^52, including NaN, +Inf and -Inf. This is the hold class: the whole bar changes no state and the prior extreme is repeated, or NaN if no column has started. Deleting missing bars preserves values on the remaining bars bit for bit; a low above its high is used as given.

There is no warm-up: the first valid index is 0. NaN is the data-dependent state before the first column, including bar 0; dir.EQ(0) detects it. This causal latch is not start-invariant: starting later can choose different columns. Once active runs share a direction and extreme, their extremes agree thereafter. Prefixes and repeated calls agree bit for bit, and later bars do not revise earlier values.

The calculation is one pass, O(1) per bar regardless of the number of boxes, allocates only the result and does not change either input, including when both are the same slice.

Recipes built on the PointAndFigure methods are listed under “Compositions” in the “Point and Figure” category.

PointAndFigureResistance

func (s Series) PointAndFigureResistance(low Series, box float64, reversal int) Series

PointAndFigureResistance returns the top of the most recently completed column of Xs, held until the next completes, and NaN until one has.

PointAndFigureDirection defines the grid anchored at 0 with level prices k*box rounded once in the receiver’s units and the rule for highs s and lows low: a column extends first, else reverses, and the first column waits for the reversal span and exactly one range extreme to move after the first usable bar.

An X column completes only on its reversal to Os. That bar posts the former X extreme as its level price k*box rounded once, in the receiver’s units, and subsequent bars hold it until another X column completes. It is NaN until that first completion; known values are finite, with level 0 as +0. In particular, the range before the first column is not a column and does not set resistance.

With e as PointAndFigureExtreme and dir as PointAndFigureDirection, the value is e.Shift(1).ValueWhen(dir.Delta().EQ(-2)), including NaN before the first event. PointAndFigureSignal sets +1 when a later X column rises strictly above this completed top, comparing levels.

With c as closes, close mode is c.PointAndFigureResistance(c, box, reversal).

Valid parameters satisfy 0 < box < +Inf and reversal >= 1; invalid parameters return len(s) zeros, all +0. Subnormal boxes and the largest finite box are valid. A reversal above 2^53 is valid but cannot start a column. Unequal lengths, checked first, return a non-nil empty Series. Equal empty inputs return a non-nil empty Series regardless of parameters.

A bar is missing if either price x fails math.Abs(x)/box < 2^52, including NaN, +Inf and -Inf. This is the hold class: the whole bar changes no state and repeats the prior resistance, or NaN if no X column has completed. Deleting missing bars preserves values on the remaining bars bit for bit; a low above its high is used as given.

There is no warm-up: the first valid index is 0. NaN represents an undetermined completed top; bar 0 has no column, as dir.EQ(0) detects. The calculation is causal and not start-invariant. Once two active runs share a direction and extreme, their resistance agrees after the next X column completes. Prefixes and repeated calls agree bit for bit, and later bars do not revise earlier values.

The calculation is one pass, O(1) per bar regardless of the number of boxes, allocates only the result and does not change either input, including when both are the same slice.

Recipes built on the PointAndFigure methods are listed under “Compositions” in the “Point and Figure” category.

PointAndFigureSignal

func (s Series) PointAndFigureSignal(low Series, box float64, reversal int) Series

PointAndFigureSignal returns Vomund’s double-top and double-bottom breakout latch: +1 from a column of Xs that rises above the previous column of Xs, -1 from a column of Os that falls below the previous column of Os, held until the opposite breakout, and 0 before the first.

PointAndFigureDirection defines the grid anchored at 0 with level prices k*box rounded once in the receiver’s units and the rule for highs s and lows low: a column extends first, else reverses, and the first column waits for the reversal span and exactly one range extreme to move after the first usable bar.

After each usable bar’s update, Xs set +1 when their extreme level exceeds the level of the completed X top reported by PointAndFigureResistance; otherwise Os set -1 when their extreme level is below the level of the completed O bottom reported by PointAndFigureSupport. A missing prior top or bottom cannot trigger a breakout. The comparisons use levels and are strict: reaching an old top or bottom does not signal. All other bars hold the latch, with +0 before the first breakout, including before the first column. A breakout needs at least three columns: one of the same kind to complete, an intervening opposite column and a later column that passes the completed extreme.

Let dir, e, res and sup be PointAndFigureDirection, PointAndFigureExtreme, PointAndFigureResistance and PointAndFigureSupport. With levels below 2^51 in magnitude, the equivalent expression comparing prices is dir.EQ(1).Mul(e.GT(res)).Sub(dir.EQ(-1).Mul(e.LT(sup))).Hysteresis(0, 0). GT and LT with NaN are false; Hysteresis holds the last non-zero decision and starts at 0.

This is the double-top and double-bottom latch from Vomund, “Classic Point And Figure”, Technical Analysis of Stocks & Commodities, December 2000. The triple top, bullish catapult, bullish signal reversal, count targets and the bullish percent of a group are not built. These rules alone demonstrate no trading edge. DoubleTop and DoubleBottom are a different pattern: the bar-chart double top and double bottom, two confirmed swings within a band, confirmed by a close beyond the neckline between them (the manual’s “Chart Patterns” section).

With c as closes, close mode is c.PointAndFigureSignal(c, box, reversal).

Valid parameters satisfy 0 < box < +Inf and reversal >= 1; invalid parameters return len(s) zeros, all +0. Subnormal boxes and the largest finite box are valid. A reversal above 2^53 is valid but cannot start a column. Unequal lengths, checked first, return a non-nil empty Series. Equal empty inputs return a non-nil empty Series regardless of parameters.

A bar is missing if either price x fails math.Abs(x)/box < 2^52, including NaN, +Inf and -Inf. This is the hold class: the whole bar changes no state and repeats the prior signal, or +0 if no breakout has occurred. Deleting missing bars preserves values on the remaining bars bit for bit; a low above its high is used as given.

There is no warm-up: the first valid index is 0. Bar 0 and every bar before the first column are undetermined, detected by dir.EQ(0); the signal can remain +0 after a column starts. The latch is causal and not start-invariant: a later start may change columns and recorded levels. Once two runs have the same breakout inputs, their signals agree from the first non-zero input. Prefixes and repeated calls agree bit for bit, and later bars do not revise earlier values.

The calculation is one pass, O(1) per bar regardless of the number of boxes, allocates only the result and does not change either input, including when both are the same slice.

Recipes built on the PointAndFigure methods are listed under “Compositions” in the “Point and Figure” category.

PointAndFigureSupport

func (s Series) PointAndFigureSupport(low Series, box float64, reversal int) Series

PointAndFigureSupport returns the bottom of the most recently completed column of Os, held until the next completes, and NaN until one has.

PointAndFigureDirection defines the grid anchored at 0 with level prices k*box rounded once in the receiver’s units and the rule for highs s and lows low: a column extends first, else reverses, and the first column waits for the reversal span and exactly one range extreme to move after the first usable bar.

An O column completes only on its reversal to Xs. That bar posts the former O extreme as its level price k*box rounded once, in the receiver’s units, and subsequent bars hold it until another O column completes. It is NaN until that first completion; known values are finite, with level 0 as +0. In particular, the range before the first column is not a column and does not set support.

With e as PointAndFigureExtreme and dir as PointAndFigureDirection, the value is e.Shift(1).ValueWhen(dir.Delta().EQ(2)), including NaN before the first event. PointAndFigureSignal sets -1 when a later O column falls strictly below this completed bottom, comparing levels.

With c as closes, close mode is c.PointAndFigureSupport(c, box, reversal).

Valid parameters satisfy 0 < box < +Inf and reversal >= 1; invalid parameters return len(s) zeros, all +0. Subnormal boxes and the largest finite box are valid. A reversal above 2^53 is valid but cannot start a column. Unequal lengths, checked first, return a non-nil empty Series. Equal empty inputs return a non-nil empty Series regardless of parameters.

A bar is missing if either price x fails math.Abs(x)/box < 2^52, including NaN, +Inf and -Inf. This is the hold class: the whole bar changes no state and repeats the prior support, or NaN if no O column has completed. Deleting missing bars preserves values on the remaining bars bit for bit; a low above its high is used as given.

There is no warm-up: the first valid index is 0. NaN represents an undetermined completed bottom; bar 0 has no column, as dir.EQ(0) detects. The calculation is causal and not start-invariant. Once two active runs share a direction and extreme, their support agrees after the next O column completes. Prefixes and repeated calls agree bit for bit, and later bars do not revise earlier values.

The calculation is one pass, O(1) per bar regardless of the number of boxes, allocates only the result and does not change either input, including when both are the same slice.

Recipes built on the PointAndFigure methods are listed under “Compositions” in the “Point and Figure” category.

Compositions

Point-and-figure recipes

The six point-and-figure views take highs h as the receiver and lows l as the first argument, a fixed box in the receiver’s units and a reversal in whole boxes. The recipes below change what the box and the inputs are, or read column facts from the views; dir, ext and b are the direction, the extreme and the boxes of one call. The direction, the boxes and every count below are whole numbers, and the extremes are level prices, so every identity marked (E) below holds bit for bit.

(a) Close-only charts

Pass the closes as both series:

dir := c.PointAndFigureDirection(c, box, 3)
ext := c.PointAndFigureExtreme(c, box, 3)
  • First valid index: 0; the chart is undetermined (direction 0, extreme NaN) on bar 0 and until the highest X level and the lowest O level of the closes are at least reversal levels apart, and dir.EQ(0) detects those bars.
  • Anchors: the call with the same slice twice equals the call with a copy of c as the lows, and the inputs are not changed (E). Closes usually draw fewer columns than highs and lows, because a close moves less than the bar’s range, although a wide outside bar can delay the high-low chart where the closes start: on the 200 AAPL bars of 2023 in the package’s test data, at box 1 and reversal 3, close mode draws 24 columns and the high-low chart 52.
  • Pinned by TestRecipePointAndFigure; runnable example ExampleSeries_PointAndFigureDirection_closeMode.

(b) Logarithmic boxes and a grid anchored at a price

Every box the same fraction p of price (Ballesca Loyo): run the methods on log prices with the box math.Log1p(p), and map a level back to a price with Exp.

lb := math.Log1p(p)
dir := h.Log().PointAndFigureDirection(l.Log(), lb, 3)
level := h.Log().PointAndFigureExtreme(l.Log(), lb, 3).Exp()
// Brown's grid, anchored at the price a instead of 1:
dirA := h.Mul(1/a).Log().PointAndFigureDirection(l.Mul(1/a).Log(), lb, 3)
levelA := h.Mul(1/a).Log().PointAndFigureExtreme(l.Mul(1/a).Log(), lb, 3).Exp().Mul(a)
  • First valid index: 0, as for the views. A price at or below 0 has a NaN or -Inf log and is a missing bar.
  • Accuracy: the level views hold k times math.Log1p(p) rounded once, in log units; .Exp() gives (1+p)^k up to the rounding of the base, magnified |k| times, within a relative (|k|+1)*2^-50.
  • Loyo’s boxes: his log10 box of 0.01 is p = 10^0.01 - 1, 2.33% a box and 7.15% a three-box reversal, and a box of 0.023026 in the natural-log units of this recipe; his log10 box of 0.005 is p = 10^0.005 - 1, 1.16% and 3.51%. Read back from the extremes of a rising series, his log10 ladders (2238.7, 2290.9, … 2884.0 at levels 335 to 346 for 0.01) and ln ladders match his Figures 4 and 5 to 0.1.
  • Brown’s WSAU chart (monthly highs and lows, February to December 1994, a 1% box, reversal 3): with a = 21.874035 the extremes of the anchored recipe, rounded to three decimals, are Brown’s printed box values 25.649, 28.902, 22.314, 25.144, 25.906, 27.499, 22.762, 25.144 and 22.093 from April on. Brown’s touch rule keeps his column of Os in May and extends it in June, where the high-low rule reverses twice; from July on the two charts agree.
  • Pinned by TestRecipePointAndFigure; runnable examples ExampleSeries_PointAndFigureDirection_logBox and ExampleSeries_PointAndFigureExtreme_brown.

(c) A box frozen from the ATR

A volatility box is a scalar read once, at the first bar where it is known, and the chart starts there:

k := 14 // the first valid index of ATRWilder(h, l, c, 14)
box := c.ATRWilder(h, l, c, 14)[k]
dir := h[k:].PointAndFigureDirection(l[k:], box, 3)
// The same values, aligned with the full series (bars before k are missing bars):
dirFull := h.SetN(k, math.NaN()).PointAndFigureDirection(l.SetN(k, math.NaN()), box, 3)
  • First valid index: bar k of the series (bar 0 of the slice); the bars before k read 0 and NaN in the aligned form.
  • Why frozen and sliced: a box read at a later bar and applied from bar 0 would let the bars before it see a box computed from later bars. A box re-read at each new box (Durenard) is not built.
  • Anchors: dirFull[k:] equals dir on every view, because a missing bar changes nothing and deleting the missing bars gives the same values on the others (E). On the AAPL bars of 2023 the box is 2.9221428571428567, and the frozen chart draws 7 columns, the first on bar 16 of the slice.
  • Pinned by TestRecipePointAndFigure; runnable example ExampleSeries_PointAndFigureDirection_atrBox.

(d) Column starts, count, height and age

dir := h.PointAndFigureDirection(l, box, 3)
b := h.PointAndFigureBoxes(l, box, 3)
starts := dir.Delta().Clamp(-1, 1)                // +1 where Xs start, -1 where Os start, the first column included
toXs, toOs := dir.Delta().EQ(2), dir.Delta().EQ(-2) // the reversals alone
count := dir.Delta().Abs().GT(0).CumSum()          // columns started so far
height := b.Abs().SumSince(dir.Delta())             // boxes in the current column
age := dir.StateAge().Mul(dir.Abs())                // bars in the current column, from 1
  • First valid index: 0 for all six. Before the first column starts, toXs, toOs, count and age are 0 and height is NaN (SumSince is NaN before its first event).
  • Events: dir.Delta() is itself an event series for the event operators (any non-zero value that is not NaN is an event), so SumSince, HighestSince and LowestSince over dir.Delta() restart on each column’s first bar, and dir.Delta().BarsSince() is age less one from the first column on.
  • Anchors: |b| >= reversal on every bar where starts is not 0, and height is at least reversal on every bar of a column (E). count is cumulative from the first bar of the series, so it drifts with the slice origin; compare counts only between runs on the same slice. age counts missing bars, on which the column holds. On StockCharts’ six-day example (box 1, reversal 3) starts is [0, -1, 0, 1, 0, -1], count [0, 1, 1, 2, 2, 3], height [NaN, 3, 4, 3, 4, 3] and age [0, 1, 2, 1, 2, 1].
  • Pinned by TestRecipePointAndFigure; runnable example ExampleSeries_PointAndFigureBoxes_columns.

Renko recipes

A renko chart on closes draws a brick of a fixed size b each time a close reaches the level one brick beyond the last brick, and turns only when a close reaches the level one brick beyond the last brick’s opposite edge: two bricks below the top of an up brick, or two above the bottom of a down brick. That is a close-only point-and-figure chart with a box of b and a reversal of 2 boxes, so the three renko series are recipes over the point-and-figure views, with the closes as both series:

dir := c.PointAndFigureDirection(c, b, 2) // the brick colour: +1 up, -1 down
level := c.PointAndFigureExtreme(c, b, 2) // the top of the last up brick, the bottom of the last down brick
d := dir.Delta()                          // +2 or -2 on a bar where the bricks turn
// The signed bricks drawn on the bar: the boxes, one fewer on a bar where the bricks turn.
bricks := c.PointAndFigureBoxes(c, b, 2).Sub(d.EQ(2)).Add(d.EQ(-2))
  • First valid index: 0. Before the first brick dir and bricks are 0 (not NaN) and level is NaN, and dir.EQ(0) detects those bars.
  • Bricks: on a turn, the new point-and-figure column counts as its first box the level one box from the old extreme (the bottom of the last up brick on a turn down), where the renko chart’s first brick in the new direction only starts. So on a bar where d is +2 or -2, bricks is one brick smaller in size than PointAndFigureBoxes, and on every other bar from the first brick on the two are equal (E). The bricks of a bar are signed: +n draws n up bricks and -n draws n down bricks.
  • The start: the grid is absolute (the multiples of b), so the levels do not depend on where the series starts. The engine’s first column waits until the closes span two boxes, from the highest floor level to the lowest ceiling level, and marks the whole span. Chart packages start at the first close and draw the first brick as soon as a close covers one whole brick of the grid beyond it, so a chart can show a brick while dir.EQ(0) is 1; from the engine’s first brick on, a standard renko loop on the same grid and the recipe agree bar for bar on the direction and the level, and on the bricks from the next bar. On 2,000 random walks of 300 closes at five brick sizes they agree on all 590,475 bars after the engine’s first brick, and on its first brick’s bar in all 2,000 runs (direction and level), while on 2,209 earlier bars the loop already shows a brick.
  • Sources: Duddella (“Trading Renko Charts”, Technical Analysis of Stocks & Commodities, August 2007), rows 6 to 10 of his Figure 3 at a brick of 1, after a two-close prefix [535.5, 533.0] that puts the chart in the same falling state: on the closes 531.8, 534.2, 531.6, 532.5 and 530.2, dir is [-1, 1, -1, -1, -1], level [532, 534, 532, 532, 531] and bricks [-1, 1, -1, 0, -1]. His caveat that a turn needs “three times the box size” is a misprint against his own Figure 3, where the top of brick D is at 534 and the bottom of brick E at 532, two bricks, and his rows 2 and 4 draw a brick on less than one brick’s move, so only rows 6 to 10 are a fixture. His five-point bricks: a close 16 points above the top of the last up brick draws 3 bricks ([1000, 990, 1000, 1016] at 5), and one 9 points below the bottom of the last down brick draws 1 ([1000, 1010, 1000, 991]). Williams (Trade Stocks and Commodities with the Insiders, 2005, chapter 14, p. 174): at five-unit bricks a 20-point rally draws four ([100, 90, 100, 120] at 5). StockCharts’ ChartSchool (“Renko Charts”): at 10-point bricks a rise from 1840 to 1849 draws nothing and 1850 one brick ([1840, 1820, 1840, 1849, 1850] at 10 gives bricks [0, -2, 1, 0, 1]); its ATR brick is read when the chart is drawn, so the chart repaints.
  • A two-brick turn: on [1840, 1820, 1840, 1850, 1831, 1830] at 10, bricks is [0, -2, 1, 1, 0, -1] where PointAndFigureBoxes is [0, -2, 2, 1, 0, -2]: the close 1830 turns the chart down from the top 1850 and draws one brick, from 1840 to 1830.
  • Highs and lows: a renko chart on each bar’s range is the same three calls with the highs as the receiver and the lows as the first argument, h.PointAndFigureDirection(l, b, 2) and so on.
  • Not built: Vervoort’s modified renko (“Exploring Charting Techniques, Part 1”, Technical Analysis of Stocks & Commodities, July 2014). His code keeps the standard thresholds; the open at the previous brick’s close and the wicks are drawing details of bricks in event time, which a Series aligned with the bars cannot carry; and his restart at each session is the recipe run on each session’s slice, with session keys the caller supplies, on the absolute grid rather than his grid anchored at the session’s first price. A brick re-read from the ATR at each new brick is not built either; a brick read once from the ATR is recipe (c) of the point-and-figure recipes, unchanged with a reversal of 2.
  • Pinned by TestRecipeRenko; runnable examples ExampleSeries_PointAndFigureDirection_renko and ExampleSeries_PointAndFigureBoxes_renkoBricks.

The OXXO state and its two column counts below read 0 before the first break, as latches do; the bands and centre lines that feed its variants are masked with NaN before their first valid index, where the methods pass the input through, and a NaN holds the latch. Prices are positive and finite and lengths are in bars; the column count starts at the first bar of the slice.

OXXO: the latched breakout and its columns (Chande)

Tushar Chande (“Charting Breakouts With Convertible Point & Figure Charts”, Stocks & Commodities V39:12, November 2021, pp. 9-16) replaces point-and-figure reversals “at trailing stops from the highest high and lowest low close” with “a price channel or a price band built around some moving average”, which “automatically guarantees that the reversal lines do not cross each other”, and advances one column for each reversal. t6OXXO latches the strict breaks of his “standard 20-day price channel … plotted with a one-day delay” with Breakout(n, 1).Hysteresis(0, 0): +1 in a rising column, -1 in a falling one, 0 before the first break; cols and age are the column views of this category’s recipe over PointAndFigureDirection. Chande’s fourth defect of point and figure, that “the reversal levels often cross over”, belongs to his simplified rule of reversals from a trailing close channel, not to this category’s engine, whose one reversal level is measured from the column’s extreme; what OXXO removes is the box size and the missing time axis.

st := c.Breakout(n, 1).Hysteresis(0, 0)
cols := st.Delta().Abs().GT(0).CumSum()
age := st.StateAge().Mul(st.Abs())
  • Inputs: closes; the channel length n (Chande’s 20).
  • First valid index: n, Breakout’s n + lag - 1 with lag 1; st, cols and age read 0 before the first break. cols counts columns from the slice’s first bar, so compare differences between runs on one slice, and age counts the bars in the column from 1, so 0 means only that no column has started.
  • Anchors: st equals a loop of Chande’s rule (a close above the highest of the previous n closes starts or continues a rising column, one below their lowest a falling one, anything else holds) and, with b := c.Breakout(n, 1), c.PositionSigned(b.EQ(1), b.EQ(-1), b.EQ(-1), b.EQ(1)), bit for bit, since b is not +1 and -1 on one bar; cols and age equal their loops; a constant series stays 0 and a strictly rising one is one rising column from bar n.
  • Traps: a latch over +-1 flags needs Hysteresis(0, 0): Hysteresis(1, -1) decides only on values strictly beyond 1 and -1, so on flags it does not move.
  • Variant: the band form, with a centre line cl and widths wl and ws: c.GT(cl.Add(wl)).Sub(c.LT(cl.Sub(ws))).Hysteresis(0, 0). Chande’s examples are a 50-day simple average plus and minus 3%, written as w := cl.Mul(0.03) with cl.Add(w) and cl.Sub(w) (cl.Mul(1.03) differs in the last bit), and c.Bollinger(20, 1) and c.Bollinger(20, -1) as the two levels; the caller masks the centre’s warm-up with NaN.
  • Variant: the trading form c.PositionSigned(up, dn, dn, up) reverses where both flags fire on one bar (highs and lows beyond the bands, for example), where the latch holds.
  • Evidence: the article has no test, no table and no code; its figures plot the S&P 500’s closes, and the evidence for a channel stop and reverse is the literature on channel rules (Szakmary, Shen and Sharma, 2010). Evidence, not a reference.
  • Pinned by TestRecipeT6OXXO; runnable example ExampleSeries_Breakout_oxxo.