Skip to content

About

Dependency-free swing-pivot market structure: HH/HL/LH/LL classification, zigzag, and named structure states from a price series.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

pivotstructure

Swing-pivot market structure from a price series: the HH / HL / LH / LL read, in dependency-free Python.

Feed it highs and lows from any source and ask the question traders actually ask about trend: is this market making higher highs and higher lows?

from pivotstructure import detect_pivots, structure_state

pivots = detect_pivots(highs, lows, lookback=10)
pivots[-1].type          # 'HL'
pivots[-1].confirmed     # False, the newest pivot can still move

state = structure_state(pivots)
state.sequence           # 'LH-HL-HH-HL'
state.family             # 'momentum_long'
state.reading            # 'a higher low after a higher high: the advance is extending, …'

Stdlib only. No numpy, no pandas, no I/O, no configuration. Python 3.12+.

Why not a moving average

A moving average is a lagging function of price: it whipsaws in a directionless market and confirms turns late. A pivot read degrades more honestly, since chop produces mixed labels, which is information rather than a false signal.

The two are complementary rather than competing. A market trading above its 200-day average while printing lower highs is a specific, nameable state that neither read expresses alone.

How it works

Three stages:

  1. Mark. Bar i is a pivot high when its high is the maximum of the trailing lookback bars including itself. The window looks only backwards, so a mark is knowable the moment the bar closes.
  2. Zigzag. Consecutive marks of the same kind collapse to the most extreme one, leaving a strictly alternating high/low sequence.
  3. Classify. Each pivot is labelled against the previous pivot of its own kind: higher high HH, lower high LH, higher low HL, lower low LL.

lookback sets how significant a swing has to be: larger windows give fewer, bigger pivots. Ten daily bars is a reasonable starting point, not a calibrated number.

The confirmed flag: read this before backtesting

The zigzag keeps replacing the newest pivot while price extends, so its index, price and label are all provisional until a pivot of the opposite kind forms. Because the sequence strictly alternates, that means:

every pivot except the last is final; the last one can still move.

Scanning a whole series and treating every pivot as though it had been knowable when it printed is look-ahead bias. It discards the false starts a live reader would have acted on, each having been overwritten by the better pivot that followed. Results built that way are optimistic in a way no amount of careful cross-validation downstream can repair.

from pivotstructure import confirmed_pivots

safe = confirmed_pivots(pivots)   # everything that can no longer change

StructureState.confirmed_through gives the bar index the state was actually settled through, which is the bar a forward-return study should date it from.

API

detect_pivots(highs, lows, lookback=10) -> list[Pivot] Detect and classify.
confirmed_pivots(pivots) -> list[Pivot] Drop the provisional one.
structure_state(pivots, length=5) -> StructureState | None Name the current shape. None when there are fewer than two pivots.
classify_pattern_family(pattern) -> str 'HL-HH-HL' → 'reversal_long'. Coarse: each family spans two opposite shapes.
reading_for(pattern) -> str What price actually did, from the last two pivots. Use this for one sentence.
sequence_string(pivots, length=5) -> str The last n labels, hyphenated.

Pivot(index, price, is_high, type, confirmed). index is a position in the input series, not a date; this package never sees a calendar.

StructureState(sequence, family, last_pivot, confirmed_through, reading).

The four families

Decided by two things: the kind of pivot the sequence ends on, and whether the last two legs travelled the same way.

ends on a low (turning up) ends on a high (turning down)
legs agree momentum_long momentum_short
legs disagree reversal_long reversal_short

A two-pivot sequence is always momentum: one leg has no prior direction to have reversed from.

long and short name the side of the terminal pivot, not the trend. LH-LL is a long family and a market making lower lows: price is at a low and the next move off it is up. Each family therefore spans a rising and a falling shape, which is why reading_for keys on the last two pivots instead.

What this is not

The family names a shape, not a recommendation. momentum_long says the last two legs both went up, nothing about what happens next. Whether any of these states carries a tradeable edge is a question about forward returns that this package does not answer, and the honest way to ask it is with confirmed pivots only.

There are no entries, exits, targets, stops, or models here, and there never will be.

Install

pip install pivotstructure

Related

  • optionstruct, a position: legs, payoff, exact risk, Decimal throughout.
  • strikemetrics, a chain's positioning: gamma exposure, max pain, walls, put/call ratios.

License

MIT

About

Dependency-free swing-pivot market structure: HH/HL/LH/LL classification, zigzag, and named structure states from a price series.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages