제법 유명한 지표인 상품 채널 지수, CCI(Commodity Channel Index)는 도널드 램버트(Donald Lambert)에 의해 개발된 상품 가격을 관찰하기 위한 지표다. 현재는 선물 뿐만이 아니라 주식 현물, 가상 화폐나 코인 등 여러 투자 자산에 적용되고 활용되고 있다. 개념은 앞서 소개했던 PGO(Pretty Good Oscillator)와 유사하다. 종가와 이동평균의 차이를 평균 편차에 0.015를 곱한 값으로 나눠준다. PGO의 경우에는 분모에 ATR을 사용했으나 CCI에서는 평균 편차를 사용하는 것이 차이점이다. CCI에서 평균 편차는 통상 가격(Typical Price)의 평균, 즉 고가, 저가, 종가의 평균을 사용하여 통상가격의 평균과 현재 통상가격의 위치와 비교한 절대 편차를 사용하는 것이므로 CCI와 개념적으로 유 사하다. 이렇다 보니 종목과 기간의 변동성이 반영되며 CCI의 값이 보정되는 효과가 있다.
다른 지표들도 대부분 그렇지만 보조지표들이 보통 상품 선물 거래를 위해 만들어진 것들이 많다. CCI 역시 이름에서 알 수 있듯 상품 선물 거래를 위해 만들어졌던 지표이고 선물 시장은 전일봉 종가가 오늘 봉의 시가다. 이렇다 보니 소위 통상 가격(Typical Price)를 사용하는 경우가 많은데 CCI도 이러한 이유로 Typical Price를 이용해 평균 편차를 산출한다.
매매 전략은 다른 보조 지표들과 유사하다. 0을 기준으로 0보다 크면 상승 모멘텀, 0보다 작으면 하락 모멘텀으로 본다. 따라서 CCI가 0을 상승 돌파하면 매수하고, CCI가 0을 하락 돌파하는 경우에 매도하는 전략이다. 이 전략은 횡보장에서 허위 시그널이 자주 발생할 수 있으나, 비교적 추세가 명확한 경우나 적당한 사이클을 두고 주가가 오라락 내리락하는 경우에는 꽤나 잘 맞는 전략이다. 애시당체 CCI가 가격 변동과 주기성을 관찰하는 목적을 가지고 만들어진 지표이다 보니 이런 경우에 이런 주기성과 사이클을 잘 포착해낸다.
다른 매매 전략은 상단, 하단 밴드를 정하고 하단 밴드를 상항 돌파하는 경우 매수, 상단 밴드를 하향 돌파하는 경우 매도를 하는 전략이다. CCI는 값의 범위가 정해져 있지 않다. 백분율을 사용하지 않는 지표들의 단점인데, 현실적으로 가능성은 적으나 이론적으로는 CCI의 값이 1000 혹은 -1000을 넘을 수도 있다는 이야기다. 보통 CCI는 -100 ~ 100 사이의 값 분포를 보이므로 통상적으로 -100을 하단, 100을 상단으로 잡고 -100 밑의 값일 경우 과매도로 판단하고 100을 넘을 경우 과매수로 판단하여 매매에 임하는 전략이다.
마지막으로 CCI(Commodity Channel Index) 트레이딩 뷰 파인 스크립트 소스와 pandas-ta 소스를 공유하며 마친다.
- CCI(Commodity Channel Index) 트레이딩 뷰 파인 스크립트 지표 소스
//@version=5
indicator(title="Commodity Channel Index", shorttitle="CCI", format=format.price, precision=2, 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=hlc3, title="Source")
LengthInput = input.int(defval=20, title="MA 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)
madLengthInput = input.int(defval=20, title="MA Length")
cInput = input.float(defval=0.015, title="Constant")
upperInput = input.int(100, title="Upper", group="Band Settings")
lowerInput = input.int(-100, title="Lower", group="Band Settings")
ma = ma(sourceInput, LengthInput, maTypeInput)
cci = (sourceInput - ma) / (cInput * ta.dev(sourceInput, LengthInput))
cciPlot = plot(cci, title="CCI", color=color.blue)
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(cciPlot, midLinePlot, 200, upperInput, top_color = color.green, bottom_color = color.new(color.green, 100), title = "Overbought Gradient Fill")
fill(cciPlot, midLinePlot, lowerInput, -200, top_color = color.new(color.red, 100), bottom_color = color.red, title = "Oversold Gradient Fill")
- CCI(Commodity Channel Index) 트레이딩 뷰 파인 스크립트 전략 소스
//@version=5
strategy(title="Commodity Channel Index", shorttitle="CCI", 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=hlc3, title="Source")
LengthInput = input.int(defval=20, title="MA 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)
madLengthInput = input.int(defval=20, title="MA Length")
cInput = input.float(defval=0.015, title="Constant")
upperInput = input.int(100, title="Upper", group="Band Settings")
lowerInput = input.int(-100, title="Lower", group="Band Settings")
ma = ma(sourceInput, LengthInput, maTypeInput)
cci = (sourceInput - ma) / (cInput * ta.dev(sourceInput, LengthInput))
cciPlot = plot(cci, title="CCI", color=color.blue)
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(cciPlot, midLinePlot, 200, upperInput, top_color = color.green, bottom_color = color.new(color.green, 100), title = "Overbought Gradient Fill")
fill(cciPlot, midLinePlot, lowerInput, -200, top_color = color.new(color.red, 100), bottom_color = color.red, title = "Oversold Gradient Fill")
strategySignal = input.string("CCI-Band", title="Strategy Indicator", options = ["CCI-Band", "CCI-0 Cross"])
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 == "CCI-Band")
if ta.crossover(cci, lowerInput)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(cci, upperInput)
strategy.close_all('매도')
if (strategySignal == "CCI-0 Cross")
if ta.crossover(cci, 0)
strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
if ta.crossunder(cci, 0)
strategy.close_all('매도')
bgcolor(strategy.position_size > 0 ? color.new(color.yellow,90) : na)
- CCI(Commodity Channel Index) pandas-ta 소스
import pandas as pd
import pandas_ta as ta
import FinanceDataReader as fdr
data = fdr.DataReader('005930')
cci = ta.cci(high=data['High'], low=data['Low'], close=data['Close'], length=14, c=0.015)
data = pd.concat([data, cci], axis=1)
data.dropna(inplace=True)