Linear Regression
All functions · nseries package
SlopePercent
func (s Series) SlopePercent(n int) Series
SlopePercent returns the n-bar least-squares slope as a percentage of the first price in its window, a scale-free trend rate per bar.
It is the percentage linear-regression slope of Markos Katsanos, Intermarket Trading Strategies (Wiley, 2008), the leg of his linear-regression-slope divergence (section 9.4, pages 127-128), in the form of his published code (Appendix A.1, page 298), “LinRegSlope(C,D1)/Abs(Ref(C,-D1+1))*100”. For j >= n-1 the output is signedPctOf(s.Slope(n)[j], s[j-n+1]): the ordinary least-squares slope of the window against bar number (Slope, in price units per bar), divided by the absolute value of the window’s first bar and then multiplied by 100, in his operation order. On a positive first bar it is (s.Slope(n)[j]/s[j-n+1])*100; on a negative first bar it divides by the absolute value, as his Abs does, so a rising negative series reads as rising. The unit is percent of the first price per bar.
The first valid index is n - 1 and the minimum length is n; every output before the first valid index is 0. An empty input, n < 2 or n > len(s) (math.MaxInt and math.MinInt included) returns a fresh all-zero series of len(s) in O(len(s)) time, without computing any slope.
It is window-local and start-invariant: each output depends only on the n values of its own window, so dropping bars from the front of s leaves every later output unchanged. Valid data: price levels, or any series whose windows start on a finite non-zero bar.
A NaN or an infinity at index p gives NaN on [max(p, n-1), p+n-1], and later outputs recover; a zero at p gives NaN at p+n-1 only, the end of the window that starts there, whose base is zero. In general a zero or non-finite first bar, a window holding a NaN or an infinity, or a non-finite result gives NaN. A flat window on a non-zero level gives exactly 0.
It is a portable trend filter, in percent per bar. Katsanos’s DAX system counts a 20-bar value below 0.2 percent per bar, with a congestion condition, as a trading range (“LRS20< .2”), and his FTSE system takes pullback buys only if the 80-bar value 15 bars earlier exceeds 0.03, which his code computes as “REF(100*LinRegSlope(C,80)/Ref(C,-79),-15) > .03”, equal to it in exact arithmetic on positive prices. Rafter’s moving slope rate of change (Stocks & Commodities 23:9), the (n+1)-bar slope divided by the price n bars back, is SlopePercent(n+1)/100 on positive prices: his quotient times 100 equals SlopePercent(n+1) bit for bit, but SlopePercent(n+1) divided by 100 recovers his quotient only in exact arithmetic. Katsanos’s soybean system normalises by the fitted line one bar before the window, not by the first price, so it is a different quantity.
Deviation from the MetaStock code: every output before the first valid index is 0, where MetaStock has no value.
The warm-up 0 fires Katsanos’s “LRS20< .2” trading-range rule on every bar before the first valid index, so trim (Window(-(n-1))) or mask (SetN(n-1, math.NaN())) the warm-up first.
Verification: two independent references, a Python reference in exact rational arithmetic and a Julia reference that also translates Katsanos’s “LinRegSlope(C,D1)/Abs(Ref(C,-D1+1))*100”, agree with it; it equals s.Slope(n)[i]/s[i-n+1]*100 bit for bit wherever the window’s first bar is positive and the result is finite; and the negation and power-of-two scaling identities hold bit for bit, apart from the sign of a zero and of a NaN: s.Mul(-1).SlopePercent(n) equals s.SlopePercent(n).Mul(-1), and a power-of-two scale of s that neither overflows nor underflows leaves the result unchanged.