Price Momentum Oscillator (Carl Swenlin, DecisionPoint) — a one-bar
percent rate of change put through two stages of DecisionPoint's custom
smoothing, scaled by ten, with a span-EMA signal line:
Appends two columns. Double-smoothing a one-bar rate of change is what
makes this readable at all — the raw series is noise — and the result is a
slow momentum line read on zero crossings and on crosses of its own signal.
The line is named ${prefix}, not ${prefix}Line — the trix
shape, with the signal keeping the family suffix.
"Custom smoothing" is α = 2/n, and it is NOT the K2 engine's ema
This is the one definition in the package that is not a span EMA.
DecisionPoint defines its smoothing multiplier as 2/n where every
other EMA in this package (and in TA-Lib, and in pandas' ewm(span=n))
uses 2/(n+1). At n = 20 that is 0.1 against 0.0952 — a
different rate, not a different seed, so it cannot be expressed as a span
and rounding it to one would ship a differently-named indicator.
Measured on the oracle input: building both stages on the span EMA instead
puts the line 0.106 away on a reading whose scale is 3.906 — about
2.7%, which is visible on a chart and invisible in a spot check. So the
study composes on alphaEmaValues, the kernel door that takes α
directly. That door exists only for this study; every other consumer
takes a span, because a span is the vocabulary the whole package and every
vendor speaks.
The signal line is a plain span EMA (α = 2/11), not a custom-smoothed
one — DecisionPoint specifies "a 10-period EMA of the PMO Line", and the
asymmetry is theirs rather than an oversight here. Measured, a
custom-smoothed signal would sit 0.088 away, the same order as the
stage difference above, so this is stated rather than left to chance.
Where the ×10 goes — nowhere, measurably
DecisionPoint writes the scaling between the two stages (customEMA(10 × stage₁, 20)) and that is what the code does, but every stage is
homogeneous, so multiplying before the first stage, between them, or
after the second gives the same numbers: measured agreement to 8.9e-16
on the oracle input, i.e. floating-point noise. Worth knowing before
someone "fixes" the placement.
No period options — the four numbers are the study
column and prefix are the whole option list. 35 / 20 / 10 with the
×10 are DecisionPoint's published PMO; a version with other lengths is a
double-smoothed ROC, which is percentChange plus two movingAverage
calls and should say so at the call site. (This is the kst
decision applied to a second fixed-parameter study — with one asymmetry
worth naming: kstdoes expose signalPeriod, because vendors differ
on it there. DecisionPoint's 10 is not similarly contested, so it stays
fixed here.)
Definition, verified
No TA-Lib function, so the oracle is a pandas replication with the
analytic first-valid bars asserted and three measured separations — the
span-EMA stages, the custom-smoothed signal, and the ×10 placement.
Warm-up
The rate of change costs bar 0; each custom stage then emits once it has
consumed its own n finite samples, stepping over the previous stage's
warm-up rather than seeding on it. So on gap-free input the line first
lands on bar 54 (1 + 35 − 1 + 20 − 1) and the signal on bar 63.
Length-preserving; each column emitted where it is defined.
Edges
Scale-invariant, not shift-invariant. The rate of change is a ratio,
so scaling every price leaves both columns unchanged; adding a constant
changes the ratio and moves them. Both pinned as property tests.
A zero previous price → undefined for that bar's rate of change,
inherited from percentChangeValues. Unreachable on prices;
reachable when column is a study output that crosses zero.
A leading gap shifts the start; an interior gap costs the bar and
the bar after it (the rate of change reads a predecessor), and the two
recursions then skip and carry on — the ema family's rule, not
Wilder's, so nothing propagates to the end.
Not bounded. It is a percent rate of change times ten, smoothed, so
its scale depends entirely on the instrument's daily moves.
Price Momentum Oscillator (Carl Swenlin, DecisionPoint) — a one-bar percent rate of change put through two stages of DecisionPoint's custom smoothing, scaled by ten, with a span-EMA signal line:
Appends two columns. Double-smoothing a one-bar rate of change is what makes this readable at all — the raw series is noise — and the result is a slow momentum line read on zero crossings and on crosses of its own signal.
The line is named
${prefix}, not${prefix}Line— the trix shape, with the signal keeping the family suffix."Custom smoothing" is
α = 2/n, and it is NOT the K2 engine'semaThis is the one definition in the package that is not a span EMA. DecisionPoint defines its smoothing multiplier as
2/nwhere every other EMA in this package (and in TA-Lib, and in pandas'ewm(span=n)) uses2/(n+1). Atn = 20that is0.1against0.0952— a different rate, not a different seed, so it cannot be expressed as a span and rounding it to one would ship a differently-named indicator.Measured on the oracle input: building both stages on the span EMA instead puts the line 0.106 away on a reading whose scale is 3.906 — about 2.7%, which is visible on a chart and invisible in a spot check. So the study composes on
alphaEmaValues, the kernel door that takesαdirectly. That door exists only for this study; every other consumer takes a span, because a span is the vocabulary the whole package and every vendor speaks.The signal line is a plain span EMA (
α = 2/11), not a custom-smoothed one — DecisionPoint specifies "a 10-period EMA of the PMO Line", and the asymmetry is theirs rather than an oversight here. Measured, a custom-smoothed signal would sit 0.088 away, the same order as the stage difference above, so this is stated rather than left to chance.Where the ×10 goes — nowhere, measurably
DecisionPoint writes the scaling between the two stages (
customEMA(10 × stage₁, 20)) and that is what the code does, but every stage is homogeneous, so multiplying before the first stage, between them, or after the second gives the same numbers: measured agreement to 8.9e-16 on the oracle input, i.e. floating-point noise. Worth knowing before someone "fixes" the placement.No period options — the four numbers are the study
columnandprefixare the whole option list. 35 / 20 / 10 with the ×10 are DecisionPoint's published PMO; a version with other lengths is a double-smoothed ROC, which ispercentChangeplus twomovingAveragecalls and should say so at the call site. (This is the kst decision applied to a second fixed-parameter study — with one asymmetry worth naming:kstdoes exposesignalPeriod, because vendors differ on it there. DecisionPoint's 10 is not similarly contested, so it stays fixed here.)Definition, verified
No TA-Lib function, so the oracle is a pandas replication with the analytic first-valid bars asserted and three measured separations — the span-EMA stages, the custom-smoothed signal, and the ×10 placement.
Warm-up
The rate of change costs bar 0; each custom stage then emits once it has consumed its own
nfinite samples, stepping over the previous stage's warm-up rather than seeding on it. So on gap-free input the line first lands on bar 54 (1 + 35 − 1 + 20 − 1) and the signal on bar 63. Length-preserving; each column emitted where it is defined.Edges
undefinedfor that bar's rate of change, inherited from percentChangeValues. Unreachable on prices; reachable whencolumnis a study output that crosses zero.emafamily's rule, not Wilder's, so nothing propagates to the end.