CMO (Chande Momentum Oscillator)는 이름에서 알 수 있듯이 투샤르 샹드(Tushar Chande)에 의해 개발된 지표이다. 투샤르 샹드는 아룬 오실레이터(Aroon Oscillator), QStick, Chande Kroll Stop 등 다양한 지표를 개발한 연구자이다. CMO는 관찰 기간 내의 상승분의 합과 하락분의 합을 구하고 상승분의 합과 하락분의 합의 차이를 이 둘의 합으로 나눈 값이다. 즉, 기간 내의 순수하게 상승한 값을 기간 내 주가 움직임의 총합으로 나눈 값이다. 이는 투샤르 샹드의 표현에 따르면 순수 모멘텀(Pure Momentum)이라는 것을 포착하기 위해 만들어진 지표이며, 상대 강도 지수(RSI, Relative Strength Index)와 비슷한 접근인데, RSI의 경우에는 상승분을 상승분+하락분으로 나눈 것이고 CMO는 상승분-하락분을 상승분+하락분으로 나눈 값이기 때문에 유사한 값이 나올 수밖에 없다. 단, 하락분을 반영한 부분에서의 차이가 실제로 적용해 보면 독특한 차이를 만든다.
2024.04.28 - [주가 분석/기술적 분석] - [기술적 분석] 지표/전략 : 상대 강도 지수 (RSI, Relative Strength Index)
CMO는 RSI와 마찬가지로 그 자체의 변동성이 높아 노이즈를 제거하기 위해 이동평균을 이용한 시그널 선을 사용하는 경우가 많고, CMO를 길게 설정하였을 경우, 톱니바퀴처럼 스파이크는 발생하나 그 폭이 많이 줄어들어 기간을 길게 놓고 장기투자에 사용하는 경우도 있다. 0을 기준으로 보고 0보다 CMO가 높게 있을 경우, 강세장 혹은 상승 모멘텀이라고 보고, 0보다 CMO가 낮게 있을 경우, 약세장 혹은 하락 모멘텀이라고 본다.
일반적인 매매 방법은 RSI와 동일하다. 과매수, 과매도 구간을 정해 놓고 CMO 시그널 선이 과매도 선을 상향 돌파하면 매수, CMO 시그널 선이 과매수 선을 하향 돌파하면 매도하는 방법이다. RSI와 비슷한 지표이다 보니 RSI처럼 승률이 좋고 비슷한 시점에 시그널을 내는 경우가 많다. 일반적으로 CMO 선은 허위 시그널이 자주 발생해 매매 전략에는 사용하지 않으며 이 외에 두 개의 서로 다른 기간을 가진 CMO 선을 그려 이 선이 교차하는 시점에 매매를 하는 경우도 있다.
마지막으로 CMO (Chande Momentum Oscillator) 트레이딩 뷰 파인 스크립트 소스와 pandas-ta 소스를 공유하며 마친다.
- CMO (Chande Momentum Oscillator) 트레이딩 뷰 파인 스크립트 지표 소스
//@version=5
indicator(title="Chande Momentum Oscillator", shorttitle="CMO", format=format.price, precision=4, timeframe="", timeframe_gaps=true)
import TradingView/ta/7 as ta7
import blackcat1402/pandas_ta/7 as pta
ma(source, length, _type) =>
switch _type
"SMA" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"DEMA" => ta7.dema(source,length)
"TEMA" => ta7.tema(source,length)
"FRAMA" => ta7.frama(source,length)
"T3" => ta7.t3(source,length)
"TRIMA" => ta7.trima(source,length)
"RMA" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"HMA" => ta.hma(source, length)
"VWMA" => ta.vwma(source * volume, length)
"ALMA" => pta.alma(source, length)
"JMA" => pta.jma(source, length)
"SINWMA" => pta.sinwma(source, length)
"FWMA" => pta.fwma(source, length)
"LINREG" => pta.linreg(source, length)
"SWMA" => pta.swma(source)
"YIDYA" => pta.vidya(source, length)
"VWAP" => pta.vwap(source)
"ZLMA" => pta.zlma(source, length)
sourceInput = input(defval=close, title="Source")
lengthInput = input.int(defval=9, title="CMO Length")
signalLengthInput = input.int(defval=10, title="Signal Length")
maTypeInput = input.string(defval="SMA", title="MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], display = display.data_window)
upperInput = input.int(70, title="Upper", group="Band Settings")
lowerInput = input.int(-70, title="Lower", group="Band Settings")
momm = ta.change(sourceInput)
f1(m) => m >= 0.0 ? m : 0.0
f2(m) => m >= 0.0 ? 0.0 : -m
m1 = f1(momm)
m2 = f2(momm)
sm1 = math.sum(m1, lengthInput)
sm2 = math.sum(m2, lengthInput)
percent(nom, div) => 100 * nom / div
cmo = percent(sm1-sm2, sm1+sm2)
signal = ma(cmo, signalLengthInput, maTypeInput)
cmoPlot = plot(cmo, title="CMO", color=color.blue)
signalPlot = plot(signal, title="CMO", color=color.yellow)
upperBand = hline(upperInput, "Upper Band", color=color.gray)
hline(0, "Middle Band", linestyle=hline.style_dotted, color=color.gray)
lowerBand = hline(lowerInput, "Lower Band", color=color.gray)
fill(upperBand, lowerBand, title="Background", color=color.new(color.gray, 90))
midLinePlot = plot(0, color = na, editable = false, display = display.none)
fill(cmoPlot, midLinePlot, 100, upperInput, top_color = color.green, bottom_color = color.new(color.green, 100), title = "Overbought Gradient Fill")
fill(cmoPlot, midLinePlot, lowerInput, -100, top_color = color.new(color.red, 100), bottom_color = color.red, title = "Oversold Gradient Fill")
- CMO (Chande Momentum Oscillator) 트레이딩 뷰 파인 스크립트 지표 소스
//@version=5
strategy(title="Chande Momentum Oscillator", shorttitle="CMO", margin_long=100, margin_short=100, default_qty_type=strategy.percent_of_equity, default_qty_value=50, commission_type=strategy.commission.percent, commission_value=0.2, pyramiding=0)
import TradingView/ta/7 as ta7
import blackcat1402/pandas_ta/7 as pta
ma(source, length, _type) =>
switch _type
"SMA" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"DEMA" => ta7.dema(source,length)
"TEMA" => ta7.tema(source,length)
"FRAMA" => ta7.frama(source,length)
"T3" => ta7.t3(source,length)
"TRIMA" => ta7.trima(source,length)
"RMA" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"HMA" => ta.hma(source, length)
"VWMA" => ta.vwma(source * volume, length)
"ALMA" => pta.alma(source, length)
"JMA" => pta.jma(source, length)
"SINWMA" => pta.sinwma(source, length)
"FWMA" => pta.fwma(source, length)
"LINREG" => pta.linreg(source, length)
"SWMA" => pta.swma(source)
"YIDYA" => pta.vidya(source, length)
"VWAP" => pta.vwap(source)
"ZLMA" => pta.zlma(source, length)
sourceInput = input(defval=close, title="Source")
lengthInput = input.int(defval=9, title="CMO Length")
signalLengthInput = input.int(defval=10, title="Signal Length")
maTypeInput = input.string(defval="SMA", title="MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], display = display.data_window)
upperInput = input.int(70, title="Upper", group="Band Settings")
lowerInput = input.int(-70, title="Lower", group="Band Settings")
strategySignal = input.string("Signal-Band", title="Strategy Indicator", options = ["CMO-Band", "Signal-Band", "CMO-Signal Cross"])
momm = ta.change(sourceInput)
f1(m) => m >= 0.0 ? m : 0.0
f2(m) => m >= 0.0 ? 0.0 : -m
m1 = f1(momm)
m2 = f2(momm)
sm1 = math.sum(m1, lengthInput)
sm2 = math.sum(m2, lengthInput)
cmo = (sm1-sm2) / (sm1+sm2) * 100
signal = ma(cmo, signalLengthInput, maTypeInput)
cmoPlot = plot(cmo, title="CMO", color=color.blue)
signalPlot = plot(signal, title="CMO", color=color.yellow)
upperBand = hline(upperInput, "Upper Band", color=color.gray)
hline(0, "Middle Band", linestyle=hline.style_dotted, color=color.gray)
lowerBand = hline(lowerInput, "Lower Band", color=color.gray)
fill(upperBand, lowerBand, title="Background", color=color.new(color.gray, 90))
midLinePlot = plot(0, color = na, editable = false, display = display.none)
fill(cmoPlot, midLinePlot, 100, upperInput, top_color = color.green, bottom_color = color.new(color.green, 100), title = "Overbought Gradient Fill")
fill(cmoPlot, midLinePlot, lowerInput, -100, top_color = color.new(color.red, 100), bottom_color = color.red, title = "Oversold Gradient Fill")
startDate = input.time(defval=timestamp("01 Jan 1970 00:00 +0000"), group = "Test Range")
finishDate = input.time(defval=timestamp("31 Dec 2025 24:00 +0000"), group = "Test Range")
time_condition = time >= startDate and time <= finishDate
if(time_condition)
if (strategySignal == "CMO-Band")
if ta.crossover(cmo, lowerInput)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(cmo, upperInput)
strategy.close_all('매도')
if (strategySignal == "Signal-Band")
if ta.crossover(signal, lowerInput)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(signal, upperInput)
strategy.close_all('매도')
if (strategySignal == "CMO-Signal Cross")
if ta.crossover(cmo, signal)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(cmo, signal)
strategy.close_all('매도')
bgcolor(strategy.position_size > 0 ? color.new(color.yellow,90) : na)
- CMO (Chande Momentum Oscillator) pandas-ta 소스
import pandas as pd
import pandas_ta as ta
import FinanceDataReader as fdr
data = fdr.DataReader('005930')
cmo = ta.cmo(close=data['Close'], length=14)
data = pd.concat([data, cmo], axis=1)
data.dropna(inplace=True)