본문 바로가기
systrader79 칼럼/실전 투자 전략

실전 투자 전략 (38) - 채널 돌파 전략

by systrader79 2017. 4. 1.
728x90
반응형
728x170

 이번 시간에는 지난 포스팅에서 살펴본 여러가지 채널 돌파 전략의 시뮬레이션 결과를 확인해보겠습니다. 

 전략의 개념에 대해서는 지난 포스팅 에서 설명했으므로, 여기서는 자세한 설명은 생략하겠습니다. 궁금하신 분은 지난 포스팅 을 참고하시기 바랍니다. 


1. 채널 돌파 전략의 기초

 모든 채널 돌파 전략에서 공통적으로 고려해야 하는 요소가 3가지가 있습니다. 

  - 채널 기간 (20일, 60일, 120일 등)

  - 밴드 폭 (밴드 폭을 얼마나 넓게 설정?)

  - 매수와 매도의 기준점 (하단선, 중간선, 상단선 중 어디?)

 채널 기간은 길어질수록 장기 추세를 반영하며, 노이즈를 걸러내기는 쉽지만 가격의 움직임에 상대적으로 기민하게 반응하지 못하는 단점이 있습니다. 

 밴드 폭 또한 넓게 설정할수록 노이즈는 잘 걸러내지만, 상대적으로 신호가 늦게 발생한다는 단점이 있습니다. 

 추세 추종형 채널 돌파 전략은 상단선을 돌파할 때 매수하여, 하단선을 돌파할 때 매도하는 것이 가장 일반적입니다.

  이럴 경우 다행히 수익으로 마무리되는 경우는 아무 문제가 없지만, 운이 나빠 손실로 마무리하는 경우에는 하단선까지 뚫고 내려가는 시점까지 기다려야 하기 때문에 손실폭이 상대적으로 커진다는 단점이 있습니다. 

 이런 이유 때문에, 채널 상단 돌파시 매수, 채널 하단 돌파시 매도하는 로직 대신, 상단 돌파시 매수하되 중심선을 하향 돌파시 매도하는 전략도 많이 이용됩니다. 매도 타이밍을 좀 더 빠르게 가져감으로써 수익을 보전하고, 더 큰 손실을 막기 위해서이죠. 

 물론, 두 전략 중 어느 전략이 절대적으로 좋다는 것은 없습니다. 후자의 경우 지나치게 빨리 빠져나와서 전자보다 오히려 손실을 보는 경우도 있기 때문입니다. 

 결국, 채널 돌파 전략의 요소를 어떻게 조합하여 디자인할 것이냐는 개인의 성향과 스타일에 달린 문제이지 정답이 있는 문제는 아닙니다. 

 여기서는 가장 일반적인 채널 상단선과 채널 하단선을 매수와 매도의 기준으로 삼은 방법을 토대로 시뮬레이션 해보겠습니다. 


2. 볼린저 밴드 돌파 전략

 밴드 폭은 표준편차의 1배수로 정하고 시뮬레이션 하였습니다. 

 이전 포스팅에서 변동성 군집, 변동성 순환의 원리를 기술적인 트레이딩에 응용할 수 있다고 언급한 바 있는데, 볼린저 밴드 트레이딩 기법이 그 원리에 가장 근접한 방법 중 하나라고 볼 수 있습니다. 


3. 돈키언 채널 돌파 전략

 여기서 시뮬레이션 한 다중 돈키언 채널 돌파 전략은 매수 기준 기간은 20,60,90,120,200일이고, 매도 기준 기간은 20일로 정하였습니다. 

 돈키언 채널 돌파 전략은 '터틀 트레이딩'에서 쓰여서 유명세를 타게 된 전략이지요. 그런데 막상 뚜껑을 열어보니 어떤가요? 실망스럽지요? 대체 왜 이런 일이 일어났을까요?  

 그 이유는 돈키언 채널 돌파 전략은 기본적으로 n일간 최고가와 최저가를 밴드로 구성하는 전략이기 때문에 매매 신호의 출현 빈도가 상대적으로 적고 시장에서 머무르는 훨씬 짧은 편입니다. 이런 이유 때문에, 수익 곡선상에서는 마치 성과가 나쁜 것처럼 보일 수 있습니다. 

 시뮬레이션한 로직에서는 여러 주식 종목과 채권 종목에 분산한 상태에서, 매수 신호가 뜨지 않으면 모두 각각 현금을 보유하는 형태로 진행이 되기 때문에 현금 보유 비중이 아주 높아져서 실제 전략의 퍼포먼스보다 저평가되었을 가능성이 있습니다. 

 이처럼 신호의 출현빈도가 적은 전략을 구사할 때에는 현금 비중이 지나치게 늘어나는 것을 방지하기 위해 동일한 매매 기간 중에 신호가 먼저 발생하는 종목이 있으면 비중을 상대적으로 높이는 방법을 고려해 볼 수가 있겠습니다. 마치 듀얼 모멘텀 전략에서 절대 수익률이 높은 상위 n개의 종목을 골라 투자하는 방식처럼 말이죠.  




4. Percentile channel (stochastic channel) 돌파 전략

 앞서 살펴본 돈키언 채널이 n일 간의 최고가와 최저가를 기준으로 만든 박스권(채널)이라면 percentile channel은 이러한 최고가와 최저가로 형성되는 박스권의 일부를 취하는 채널입니다. 

 돈키언 채널의 하단부를 0, 상단부를 100으로 잡는다면, percentile channel은 예를 들어 20~80 의 값을 박스권으로 취할 수 있고, 80을 상향 돌파할 때 매수, 20을 하향 돌파시 매도하는 방식으로 트레이딩이 가능합니다. 

 Percentile channel은 돈키언 채널의 지나치게 넓은 박스권에 의한 지연된 신호를 어느 정도 보상할 수 있는 방법으로 볼 수 있습니다.  

 여기서는 박스권 전체 높이의 75% 상부 돌파시 매수, 하부 25% 이탈시 매도하는 방식으로 시뮬레이션 하였습니다. 

 


 살펴본 바와 같이 다양한 박스권 돌파 전략이 있고, 이들 전략은 나름대로의 차별점과 장단점을 가지고 있습니다. 절대적인 정답은 없으므로, 자신의 구미에 맞는 전략과 파라미터를 설정하여 원칙을 지키는 트레이딩을 하는 것이 중요합니다. 


파이썬 코드입니다.

* 다중 볼린저 밴드 돌파 전략

import pandas as pd

import matplotlib

import matplotlib.pyplot as plt

import numpy as np

from talib.abstract import *

import pandas_datareader.data as web

matplotlib.rc('font', family='Malgun Gothic',size=8, weight = 'bold')


주식리스트 = ['SPY', 'EFA', 'IWD', 'IWF', 'IJH', 'IWM', 'EWJ']

채권리스트 = ['IEF', 'TLT', 'LQD', 'IEF', 'TLT', 'LQD', 'IEF']

현금리스트 = ['SHY']

전체리스트 = 주식리스트 + 채권리스트 + 현금리스트



def 주가(stock, start):

    주가 = web.DataReader(name=stock, data_source='yahoo', start=start)['Adj Close']

    a = pd.DataFrame(주가.div(주가.iat[0]))

    a.columns = [stock]

    return a

    

def 볼린저(데이터, 매수기간, 역치):

    a = pd.DataFrame()

    b = list(데이터)

    for i in b:

        a[i] = 데이터.iloc[:,0]

        a[i+'매수시그널'] = np.where(a[i]>SMA(데이터, timeperiod=매수기간, price=i).shift(1)+역치*a[i].shift(1).rolling(20).std(),1,0)

        a[i+'매도시그널'] = np.where(a[i]<SMA(데이터, timeperiod=매수기간, price=i).shift(1)-역치*a[i].shift(1).rolling(20).std(),1,0)

        a[i+'마켓포지션'] = np.where(a[i+'매수시그널']==1,1,np.where(a[i+'매도시그널']==1,0,np.nan))

        a[i+'최종포지션'] = a[i+'마켓포지션'].ffill()

        a[i+'수익'] = np.where(a[i+'최종포지션'].shift(1) == 1, a[i]/a[i].shift(1),1).cumprod()

    return a[i+'수익']

 

def 다중볼린저(데이터, 주기1, 주기2, 주기3, 주기4, 주기5, 역치):

    a = pd.DataFrame()

    b = [주기1, 주기2, 주기3, 주기4, 주기5]

    for i in b:

        a[str(i)] = 볼린저(데이터, i, 역치)

    return a.mean(axis=1)


def 전체데이터():

    a = pd.DataFrame()

    for i in 전체리스트:

        a = pd.concat([a,주가(i,'2003-01-01')],axis=1)

    return a


a = pd.DataFrame()

for i in 전체리스트:

    a = pd.concat([a,다중볼린저(주가(i,'2003-01-01'),20,60,90,120,200,1)],axis=1)

a['mean'] = a.mean(axis=1)


전체데이터().mean(axis=1)

b = 전체데이터().mean(axis=1).to_frame()

b.columns = ['a']

다중볼린저(b,20,60,90,120,200,1)



x = pd.DataFrame()

x = pd.concat([x, a['mean'],전체데이터().mean(axis=1),다중볼린저(b,20,60,90,120,200,1)],axis=1)

x.columns = ['종목다중평균','전체평균','전체다중평균']


x.plot()

plt.show()


* 다중 돈키언 채널 돌파 전략

import pandas as pd

import matplotlib

import matplotlib.pyplot as plt

import numpy as np


import pandas_datareader.data as web

matplotlib.rc('font', family='Malgun Gothic',size=8, weight = 'bold')


주식리스트 = ['SPY', 'EFA', 'IWD', 'IWF', 'IJH', 'IWM', 'EWJ']

채권리스트 = ['IEF', 'TLT', 'LQD', 'IEF', 'TLT', 'LQD', 'IEF']

현금리스트 = ['SHY']

전체리스트 = 주식리스트 + 채권리스트 + 현금리스트



def 주가(stock, start):

    주가 = web.DataReader(name=stock, data_source='yahoo', start=start)['Adj Close']

    a = pd.DataFrame(주가.div(주가.iat[0]))

    a.columns = [stock]

    return a

    

def 돈키언(데이터, 매수기간, 매도기간=20):

    a = pd.DataFrame()

    b = list(데이터)

    for i in b:

        a[i] = 데이터.iloc[:,0]

        a[i+'매수시그널'] = np.where(a[i]>a[i].rolling(매수기간).max().shift(1),1,0)

        a[i+'매도시그널'] = np.where(a[i]<a[i].rolling(매도기간).min().shift(1),1,0)

        a[i+'마켓포지션'] = np.where(a[i+'매수시그널']==1,1,np.where(a[i+'매도시그널']==1,0,np.nan))

        a[i+'최종포지션'] = a[i+'마켓포지션'].ffill()

        a[i+'수익'] = np.where(a[i+'최종포지션'].shift(1) == 1, a[i]/a[i].shift(1),1).cumprod()

    return a[i+'수익']


def 다중돈키언(데이터, 주기1, 주기2, 주기3, 주기4, 주기5, 매도):

    a = pd.DataFrame()

    b = [주기1, 주기2, 주기3, 주기4, 주기5]

    for i in b:

        a[str(i)] = 돈키언(데이터, i, 매도)

    return a.mean(axis=1)


def 전체데이터():

    a = pd.DataFrame()

    for i in 전체리스트:

        a = pd.concat([a,주가(i,'2003-01-01')],axis=1)

    return a


a = pd.DataFrame()

for i in 전체리스트:

    a = pd.concat([a,다중돈키언(주가(i,'2003-01-01'),20,60,90,120,200,20)],axis=1)

a['mean'] = a.mean(axis=1)


전체데이터().mean(axis=1)

b = 전체데이터().mean(axis=1).to_frame()

b.columns = ['a']

다중돈키언(b,20,60,90,120,200,20)



x = pd.DataFrame()

x = pd.concat([x, a['mean'],전체데이터().mean(axis=1),다중돈키언(b,20,60,90,120,200,20)],axis=1)

x.columns = ['종목다중평균','전체평균','전체다중평균']


x.plot()

plt.show()


* 다중 Percentile channel 돌파 전략

import pandas as pd

import matplotlib

import matplotlib.pyplot as plt

import numpy as np

from talib.abstract import *

import pandas_datareader.data as web

matplotlib.rc('font', family='Malgun Gothic',size=8, weight = 'bold')


주식리스트 = ['SPY', 'EFA', 'IWD', 'IWF', 'IJH', 'IWM', 'EWJ']

채권리스트 = ['IEF', 'TLT', 'LQD', 'IEF', 'TLT', 'LQD', 'IEF']

현금리스트 = ['SHY']

전체리스트 = 주식리스트 + 채권리스트 + 현금리스트



def 주가(stock, start):

    주가 = web.DataReader(name=stock, data_source='yahoo', start=start)['Adj Close']

    a = pd.DataFrame(주가.div(주가.iat[0]))

    a.columns = [stock]

    return a

    

def 스토캐스틱(데이터, 매수기간):

    a = pd.DataFrame()

    b = list(데이터)

    for i in b:

        a[i] = 데이터.iloc[:,0]

        a[i+'매수시그널'] = np.where(a[i]>a[i].rolling(매수기간).min()+0.75*(a[i].rolling(매수기간).max()-a[i].rolling(매수기간).min()),1,0)

        a[i+'매도시그널'] = np.where(a[i]<a[i].rolling(매수기간).min()+0.25*(a[i].rolling(매수기간).max()-a[i].rolling(매수기간).min()),1,0)

        a[i+'마켓포지션'] = np.where(a[i+'매수시그널']==1,1,np.where(a[i+'매도시그널']==1,0,np.nan))

        a[i+'최종포지션'] = a[i+'마켓포지션'].ffill()

        a[i+'수익'] = np.where(a[i+'최종포지션'].shift(1) == 1, a[i]/a[i].shift(1),1).cumprod()

    return a[i+'수익']


    

def 다중스토캐스틱(데이터, 주기1, 주기2, 주기3, 주기4, 주기5):

    a = pd.DataFrame()

    b = [주기1, 주기2, 주기3, 주기4, 주기5]

    for i in b:

        a[str(i)] = 스토캐스틱(데이터, i)

    return a.mean(axis=1)


def 전체데이터():

    a = pd.DataFrame()

    for i in 전체리스트:

        a = pd.concat([a,주가(i,'2003-01-01')],axis=1)

    return a


a = pd.DataFrame()

for i in 전체리스트:

    a = pd.concat([a,다중스토캐스틱(주가(i,'2003-01-01'),20,60,90,120,200)],axis=1)

a['mean'] = a.mean(axis=1)


전체데이터().mean(axis=1)

b = 전체데이터().mean(axis=1).to_frame()

b.columns = ['a']

다중스토캐스틱(b,20,60,90,120,200)



x = pd.DataFrame()

x = pd.concat([x, a['mean'],전체데이터().mean(axis=1),다중스토캐스틱(b,20,60,90,120,200)],axis=1)

x.columns = ['종목다중평균','전체평균','전체다중평균']


x.plot()

plt.show()

나가시기 전에 블로그 글 맨 하단의 배너 광고를 한 번만 클릭해주시면 더 좋은 콘텐츠를 만드는데 큰 도움이 됩니다~ 감사합니다! >

1. 네이버 카페 '실전주식투자 연구소' (클릭) 으로 오시면, 본 블로그의 모든 내용을 순서대로 확인하실 수 있고, 다양한 실전 투자 정보도 얻을 수 있습니다~


2. 'systrader79의 주식 단기 매매 전략 온라인 강좌'가 뉴지스탁에서 진행 중입니다!

   개별 주식을 이용한 단기 매매 기법, 뉴지스탁을 통한 완전 자동 투자 매매 구현에 관한 폭넓         은 노하우를 다루고 있으니, 많은 성원 부탁드립니다~

   첫 번째 강의는 수강 신청없이 무료로 시청 가능합니다 (아래 링크 클릭 --> 1번 방송 클릭)

     강의 소개 (클릭)

     * 강의 바로가기 (클릭)



3. 여러분의 인생이 걸린 너무나도 중요한 소식! --- > 여기를 클릭하세요!


728x90
반응형
그리드형

댓글