[기술적 분석] 지표/전략 : PPO(Percentage Price Oscillator)

반응형

 

  MACD(Moving Average Convergence Divergence)를 만든 제럴드 아펠(Gerald Appel)이 만든 PPO(Percentage Price Oscillator)를 소개한다. PPO는 MACD와 거의 유사하다. 다만, MACD의 특성상 장/단기 이동 평균선의 차이를 그대로 표시하기 때문에 생기는 단점인, 주당 가격이 높은 종목의 MACD 값은 높게 나오고, 주당 가격이 낮은 종목의 MACD 값이 낮게 나오는 문제를 해결하기 위해 만들어진 지표이다. PPO는 MACD와 다르게 장/단기 이동 평균선 간의 차이를 장기 이동 평균선 값으로 나눈다. 이로서 가격차이를 백분률(Percentage)로 표시할 수 있게 되고, 이 때문에 이 지표의 이름이 Percentage Price Oscillater이다.

 

  기본적인 운용은 MACD와 유사하다. 통상 지수 이동 평균을 사용하나, 장기 이동 평균의 경우에는 30일, 단기 이동 평균의 경우에는 10일을 쓰는 경우가 있다. 보통은 MACD와 동일하게 26일, 12일을 사용한다. MACD와 마찬가지로 9일짜리 PPO의 이동평균이 시그널이 존재하며 PPO와 PPO의 이동평균이 시그널 간의 차이를 히스토그램 형태로 표현한다. 기본적으로 MACD와 계산 방식이 거의 유사하고 값을 표현할 때 장기 이동 평균선을 이용하여 스케일링 한다는 점만 차이가 있어 표현되는 모양은 거의 흡사하다.

 

MACD(Moving Average Convergence Divergence)와 PPO(Percentage Price Oscillator) 비교

반응형

 

  매매 전략은 MACD를 이용한 매매전략과 동일하다. PPO라인이 양수, 음수로 전환될 때 거래를 하는 방식. PPO에서 PPO라인은 MACD와 마찬가지로 단기 이동 평균이 장기 이동 평균보다 높은 가격을 형성하고 위쪽에 있는 경우이다. 이때는 상승 추세로 보고 PPO가 기준선(0)을 상향 돌파하면 상승 추세 전환으로 보고 매수(골든 크로스), PPO가 기준선(0)을 하향 돌파하면 하락 추세 전환으로 보고 매도(데드 크로스)한다.

 

  다음은 PPO와 시그널 라인이 교차하는 경우에 거래하는 크로스오버 방식이 있다. PPO 라인이 시그널 라인을 상향 돌파하면(히스토그램이 양수이면) 매수, PPO 라인이 시그널 라인을 하향 돌파하면(히스토그램이 음수이면) 매도하는 방식이다. 이 역시 MACD와 동일하게 수렴과 발산이 시작되는 지점을 PPO와 시그널 라인이 교차하는 지점으로 포착한다.

 

  다이버전스는 매매전략에는 적용하기 어려우나 추세전환을 미리 예측하기 좋은 방법이다. 보조지표의 시그널은 주가 변화에 후행하는데 다이버전스는 선행하여 예측할 수 있는 방법이다. 주가와 PPO 및 시그널의 방향이 다른 경우, 즉, 주가는 상승 중인데 PPO와 시그널은 고점을 갱신하지 못하고 하락하는 경우나 주가는 하락 중인데 PPO와 시그널은 저점을 갱신하지 못하고 상승하는 경우(이동평균 간의 간격이 좁아지는 경우, 수렴하는 경우)에는 조만간 추세전환이 발생할 것으로 예측할 수 있다. 

 

 

  MACD와 달리 PPO는 장/단기 이동 평균선의 차이를 종목별 주당 가격에 상관 없이 일정하게 스케일링했기 때문에 계량화하여 표현하고 비교할 수 있다. 이 때문에 상하단 밴드를 이용한 매매법 역시 적용할 수 있는데, 예를 들어, PPO, Signal 혹은 이 둘간의 차이인 히스토그램이 -1을 상향 돌파하면 과매도에서 추세전환으로 보며 매수, 반대로 1 위에 있다가 하향 돌파하면 과매수에서 추세전환으로 보고 매도하는 방식이다. 이 경우에는 시그널을 이용한 매매법이 수익률이 다른 것들에 비해 보통 높게 나오는 편이다..

 

  PPO는 MACD가 가지고 있는 단점을 해결한 수렴 발산 지표 중 완성형 지표라고 볼 수도 있다. 다만 모든 보조지표가 그렇듯 후행성의 문제와 허위 시그널에 유의해야 하며 그리고 종잡을 수 없는 종목에 대해서는 이동평균선의 길이를 조정해야 적용하는데 무리가 없을 수 있다. 그럼에도 불구하고 경향과 추세를 파악하는데 용이하며 제법 의미있는 데이터를 제공한다는 것은 부정할 수 없는 강력한 지표이다.

 

  마지막으로 트레이딩 뷰 파인 스크립트 소스와 pandas-ta 소스를 공유하며 마친다.

 

  • PPO(Percentage Price Oscillator) 트레이딩 뷰 파인 스크립트 지표 소스
//@version=5
indicator(title="Percentage Price Oscillator", shorttitle="PPO", 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)

srcInput = input(close, "Source")
fastLengthInput = input.int(12, title="Fast Length", group="Fast", minval=1)
fastmaTypeInput = input.string("EMA", title="Fast MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="Fast", display = display.data_window)
slowLengthInput = input.int(26, title="Slow Length", group="Slow", minval=1)
slowmaTypeInput = input.string("EMA", title="Fast MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="Slow", display = display.data_window)
signalLengthInput = input.int(9, title="Slow Length", group="Signal", minval=1)
signalmaTypeInput = input.string("EMA", title="Fast MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="Signal", display = display.data_window)
upperInput = input.int(1, title="Upper Band", group="Band")
lowerInput = input.int(-1, title="Lower Band", group="Band")

fastMA = ma(srcInput, fastLengthInput, fastmaTypeInput)
slowtMA = ma(srcInput, slowLengthInput, slowmaTypeInput)

PPO = ( fastMA - slowtMA ) / slowtMA * 100
signal = ma(PPO, signalLengthInput, signalmaTypeInput)
histogram = PPO - signal

plot(PPO, title="PPO", color=color.blue)
plot(signal, title="Signal", color=color.red)
hline(0, "Middle Band", linestyle=hline.style_dotted, color=color.gray)
plot(histogram, title = "Histogram", style = plot.style_columns, color = (histogram >= 0 ? (histogram[1] < histogram ? #26A69A : #B2DFDB) : (histogram[1] < histogram ? #FFCDD2 : #FF5252)))
upperBand = hline(upperInput, "Upper Band", linestyle=hline.style_dotted, color=color.gray)
lowerBand = hline(lowerInput, "Lower Band", linestyle=hline.style_dotted, color=color.gray)
fill(upperBand, lowerBand, title="Background", color=color.new(color.gray, 80))

 

 

  • PPO(Percentage Price Oscillator) 트레이딩 뷰 파인 스크립트 전략 소스
//@version=5
strategy(title="Percentage Price Oscillator", shorttitle="PPO", 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)

srcInput = input(close, "Source")
fastLengthInput = input.int(12, title="Fast Length", group="Fast", minval=1)
fastmaTypeInput = input.string("EMA", title="Fast MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="Fast", display = display.data_window)
slowLengthInput = input.int(26, title="Slow Length", group="Slow", minval=1)
slowmaTypeInput = input.string("EMA", title="Fast MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="Slow", display = display.data_window)
signalLengthInput = input.int(9, title="Slow Length", group="Signal", minval=1)
signalmaTypeInput = input.string("EMA", title="Fast MA Type", options = ["SMA", "EMA", "DEMA", "TEMA", "FRAMA", "T3", "TRIMA", "RMA", "WMA", "HMA", "VWMA", "ALMA", "JMA", "SINWMA", "FWMA", "LINREG", "SWMA", "VIDYA", "VWAP", "ZLMA"], group="Signal", display = display.data_window)
upperInput = input.float(1.0, title="Upper Band", group="Band")
lowerInput = input.float(-1.0, title="Lower Band", group="Band")
indi = input.string("Golden/Dead Cross(PPO)", title="Strategy Indicator", options = ["Golden/Dead Cross(PPO)", "Golden/Dead Cross(Signal)", "PPO-Signal", "PPO-Band", "Signal-Band", "Histogram-Band"])

fastMA = ma(srcInput, fastLengthInput, fastmaTypeInput)
slowtMA = ma(srcInput, slowLengthInput, slowmaTypeInput)

PPO = ( fastMA - slowtMA ) / slowtMA * 100
signal = ma(PPO, signalLengthInput, signalmaTypeInput)
histogram = PPO - signal

plot(PPO, title="PPO", color=color.blue)
plot(signal, title="Signal", color=color.red)
hline(0, "Middle Band", linestyle=hline.style_dotted, color=color.gray)
plot(histogram, title = "Histogram", style = plot.style_columns, color = (histogram >= 0 ? (histogram[1] < histogram ? #26A69A : #B2DFDB) : (histogram[1] < histogram ? #FFCDD2 : #FF5252)))
upperBand = hline(upperInput, "Upper Band", linestyle=hline.style_dotted, color=color.gray)
lowerBand = hline(lowerInput, "Lower Band", linestyle=hline.style_dotted, color=color.gray)
fill(upperBand, lowerBand, title="Background", color=color.new(color.gray, 80))

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 (indi == "Golden/Dead Cross(PPO)")
		if ta.crossover(PPO, 0)
			strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
		if ta.crossunder(PPO, 0)
			strategy.close_all('매도')
	if (indi == "Golden/Dead Cross(Signal)")
		if ta.crossover(signal, 0)
			strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
		if ta.crossunder(signal, 0)
			strategy.close_all('매도')
    if (indi == "PPO-Signal")
		if ta.crossover(PPO, signal)
			strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
		if ta.crossunder(PPO, signal)
			strategy.close_all('매도')
    if (indi == "PPO-Band")
		if ta.crossover(PPO, lowerInput)
			strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
		if ta.crossunder(PPO, upperInput)
			strategy.close_all('매도')
    if (indi == "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 (indi == "Histogram-Band")
		if ta.crossover(histogram, lowerInput)
			strategy.entry("매수", strategy.long, oca_type=strategy.oca.cancel, comment="매수")
		if ta.crossunder(histogram, upperInput)
			strategy.close_all('매도')
	
bgcolor(strategy.position_size > 0 ? color.new(color.yellow,90) : na)

 

 

  • PPO(Percentage Price Oscillator) 트레이딩 뷰 파인 스크립트 지표 소스
import pandas as pd
import pandas_ta as ta
import FinanceDataReader as fdr

data = fdr.DataReader('005930')
ppo = ta.ppo(close=data['Close'], fast=12, slow=26, signal=9, mamode="EMA")
data = pd.concat([data, ppo], axis=1)
data.dropna(inplace=True)
반응형