Skip to content
Open RaamTrade

KST — Know Sure Thing

Pring’s summed momentum: four rates of change on rising horizons, each smoothed, then weighted 1/2/3/4 so the slowest carries the most weight. Designed for long-horizon turns on weekly and monthly bars.

Signature

KST(roc1, roc2, roc3, roc4, on=<source>)
Argument Type Required Notes
roc1 int Param or literal Yes Fastest ROC. Default 10, bounds 1–200.
roc2 int Param or literal Yes Default 15, bounds 1–200.
roc3 int Param or literal Yes Default 20, bounds 1–200.
roc4 int Param or literal Yes Slowest ROC; also sizes the buffer. Default 30, bounds 1–400.
on= symbol, OHLCV field, or indicator No Source. Defaults to the trade symbol’s close.

Formula

$$ \text{KST} = s_1 + 2s_2 + 3s_3 + 4s_4, \quad s_i = \text{EMA}(\text{ROC}_i) $$

with the first three smoothed over 10 bars and the slowest over 15; the signal line is a 9-period EMA of the KST.

Warmup

roc4 + 1 bars before the slowest ROC exists, plus the smoothing — roughly roc4 + 30.

Reading the value

kst.kst
kst.signal
kst.kst > kst.signal

Multi-value: cannot be chained onto or crossed.

Example

class KstCross(Strategy):
"""Long-horizon entry on a KST/signal cross above zero."""
trade = Symbol()
sizing = FixedCapital()
def execute(self):
kst = KST(10, 15, 20, 30, on=self.trade)
if kst.kst > kst.signal and kst.kst > 0 and self.trade.is_flat:
self.buy(self.trade, reason=f"KST {kst.kst:.1f}")
if kst.kst < kst.signal and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="KST below signal")

Notes

  • Martin Pring. The 10/10/10/15 smoothing lengths and 1/2/3/4 weights are his; only the four ROC horizons are exposed as parameters, to stay inside the five-parameter ceiling.
  • Proven by an assert-via-trade run on SPY daily (finite and in range on every bar of a 140-bar run; zero alarm trades).
  • Built for weekly/monthly data. On daily bars it is very slow.