TradingView 트레이딩뷰로 매매 전략 만들고 백테스팅 하기 : 볼린저 밴드 단점 보완

반응형

  이전 포스팅에서는 기본 제공 보조지표, 커뮤니티 보조지표를 살펴보고 파인 스크립트로 지표를 만들어서 추가해 봤는데, 이번에는 비슷한 방법으로 나만의 매매 전략을 만들고 이걸로 백테스팅해서 성과평가까지 해보려고 한다. 트레이딩뷰의 장점인 파인 스크립트와 이를 이용해 커뮤니티에 발행된 지표/전략을 이용하면 쉽게 매매 전략을 만들 수 있고, 강력한 백테스팅 툴과 보고서를 제공한다.

 

  우선 지표 추가 시와 동일하게 기본 제공되는 전략을 한번 가져와보겠다. 화면 상단 '지표' 버튼을 누르고 나오는 팝업 좌측의 '테크니컬즈'를 누른다. 이러면 트레이딩뷰에서 제공하는 지표와 전략들이 나온다. 여기서 '전략' 탭을 선택해 보자. 트레이딩 뷰에서 제공하는 전략들을 시장에 널리 알려진 일반적인 전략들이다. 이 중에서 '볼린저 밴드 전략'을 선택해서 테스트해 보자.

  선택을 하면 아래처럼 전략에 대한 백테스트 및 성과평가가 나온다. 전략 오버뷰를 보면 순익이 0.11%로 형편없다. 근데 하다 보면 손실이 안 난 게 얼마나 대단한 건지 알게 될 거다. 이외에 성과요약에서 보면 평균 거래라던가 오버뷰에 대한 세세한 내역이 나오고 거래 목록은 전략에 의해 발생한 거래 리스트를 볼 수 있다.

 

 

  우선 전략에서 설정을 눌러보자.

  설정에서 속성을 보면 초기 자본금이나 수수료 그리고 오더 수량 등을 설정할 수 있다. 오더 사이즈를 보면 1 계약씩 주문이 나가게 되어 있는데 이게 선물에도 사용할 수 있다 보니 계약이라는 용어를 쓰는 듯하다. 매수 신호가 발생할 때마다 한 주씩 사고팔았다는 이야기. 계약을 '자기자본 %'로 바꾸고 사이즈를 50으로 설정해 주자. 예수금 절반씩은 주문을 내야지.. 피라미딩은 현재 포지션이 있을 때(이미 매수를 했을 때), 매수 시그널이 발생하면 또 매수를 날리는 것. 이건 0으로 해주자. 커미션은 증권거래세 + 농어촌 특별세 + 증권사 수수료를 대충 더해서 0.2% 정도 넣어주자. 오늘을 일봉으로 테스트할 건데, 일봉의 경우 시그널 발생 후 다음 봉 시초가에 매수하니까 동호가를 감안하면 슬리피지는 없다고 보면 된다. 시스템 트레이딩으로 단타 거래를 할 때 이 슬리피지라는게 엄청나게 영향을 주게 되는데 일단 오늘은 넘어간다. 롱/숏 포지션 증거금은 선물 용도인데 현재 얘는 현물인지 선물인지 모를 테니 증거금을 100%로 해준다.

  바꾼 내용을 저장하고 성과 평가 오버뷰를 다시 보자. 순식간에 개박살 나는 걸 알 수 있다.

  볼린저 밴드 Bollinger Bands는 박스권 장에서는 기가 막히게 수익을 내지만 방향성을 가지고 급격하게 움직이면 상승 시에는 상승 초입에 매도 후 손 놓고 보고 있고 하락장에서는 상단에 사서 하단에서 팔면서 돈을 엄청나게 까먹는다. 오늘은 여기에 트렌드를 추가하여 단점을 보완해 보겠다. 우선 보조 지표에서 했던 것처럼 불러온 볼린저 밴드 스크립트 소스코드를 열고 복사본을 만들자.

  '작업 복사본 만들기'를 누르자.

  아래가 원본 코드다.

 

//@version=5
strategy("Bollinger Bands Strategy", overlay=true)
source = close
length = input.int(20, minval=1)
mult = input.float(2.0, minval=0.001, maxval=50)
basis = ta.sma(source, length)
dev = mult * ta.stdev(source, length)
upper = basis + dev
lower = basis - dev
buyEntry = ta.crossover(source, lower)
sellEntry = ta.crossunder(source, upper)
if (ta.crossover(source, lower))
	strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="BBandLE")
else
	strategy.cancel(id="BBandLE")
if (ta.crossunder(source, upper))
	strategy.entry("BBandSE", strategy.short, stop=upper, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="BBandSE")
else
	strategy.cancel(id="BBandSE")
//plot(strategy.equity, title="equity", color=color.red, linewidth=2, style=plot.style_areabr)

  이 전략에서 우선 볼린저 밴드 지표가 차트에 보이지 않는다. 이걸 추가해 주자. 위에서 계산한 상단, 하단, 그리고 기준 이동평균선을 plot 명령어를 사용해서 차트에 오버레이 해서 표시한다. color는 원하는 색상으로 지정해 주면 된다. 나는 밴드 중간에 연하게 색을 넣어주고 싶어서 fill을 사용해서 상단과 하단 사이를 투명도 80%로 채워 줬다. 위치는 lower = basis -dev 밑에 넣는다.

plot(basis, color=color.yellow)
bbupper = plot(upper, color=color.blue)
bblower = plot(lower, color=color.blue)
fill(bbupper, bblower, color=color.new(color.blue,80))

  소스를 수정하고 하단 스크립트 영역 우측 상단에 있는 버튼 중에 '차트에 넣기'를 누른다.

  이제 차트에 볼린저 밴드가 표시된다. 왜 매수 / 매도가 나왔는지 파악하기 쉬워졌다.

  아, 트레이딩 뷰에서는 초록색 봉이 상승, 빨간색 봉이 하락이다.(미국식) 마찬가지로 파랑 위쪽 화살표가 매수 시점, 빨강 아래쪽 화살표가 매도 시점이다. 말이 나온 김에 주식을 보유하고 있는 기간도 차트에 표현해 보자. 나는 노란색에 투명도를 90% 줘서 넣겠다. 색깔을 각자 취향대로 넣는다. 아래 코드는 스크립트 코드의 맨 아랫줄에 추가해라.

bgcolor(strategy.position_size > 0 ? color.new(color.yellow,90) : na)

  마찬가지로 '차트에 넣기'를 눌러 반영한다.

  좀 더 알아보기 좋아졌다. 근데 이상한 게 하나 있다. BBandLE 밑에 +2 가 2주를 매수했다는 건데, BBandSE에는 -1이 있다. 한주만 매도했다는 뜻. 이유는 자기자본 비율로 오더 수량을 세팅해 놨는데, 스크립트에는 그때그때 주문 수량을 계산하기 때문. 이걸 현재 포지션 수량을 전부 청산하는 걸로 바꿔보자.

if (ta.crossover(source, lower))
	strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="BBandLE")
else
	strategy.cancel(id="BBandLE")
if (ta.crossunder(source, upper))
	strategy.entry("BBandSE", strategy.short, stop=upper, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="BBandSE")
else
	strategy.cancel(id="BBandSE")

  이 부분을 아래처럼 바꿔주자. BBandLE, BBandSE도 각각 매수, 매도로 바꿔 줬다.

if (ta.crossover(source, lower))
	strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="매수")
else
	strategy.cancel(id="BBandLE")
if (ta.crossunder(source, upper) and strategy.position_size > 0)
	strategy.close_all('매도')

  이 부분만 바꾸고 전략 테스트를 다시 한번 눌러보면, 수익이 엄청 좋아져 있다.

  이제 본격적으로 트렌드를 반영해 보겠다. 우선 저 노란색 기준선은 이동평균선이다. 이 이동평균선의 기울기가 양수면 상승 추세, 음수면 하락 추세로 판단하겠다. 상승 추세에서는 하단 밴드가 아니라 기준선에 닿으면 매수를 날릴 거다. 하락 추세에서는 상단 밴드가 아닌 기준선에 닿으면 매도 주문을 낼 거다. 아까 바꿨던 부분을 다시 바꿔주자.

slope = (basis[0] - basis[length]) / basis[length]
buyEntry = ta.crossunder(source, lower) or (ta.crossunder(source, basis) and slope > 0)
sellEntry = ta.crossover(source, upper) or (ta.crossover(source, basis) and slope < 0)

if buyEntry
	strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="매수")
if sellEntry
	strategy.close_all('매도')

  slope라는 변수에 기울기를 담아서 매수/매도 주문을 넣는 조건을 추가했다. 이 상태로 전략 테스터를 돌려보자.

  짠! 여기서 끝일까? 이동평균 길이나 이동평균 종류 등을 바꿔서 테스트해 볼 수도 있다. 그럼 그때마다 소스를 수정할까? 이걸 전략 설정에서 바꿀 수 있도록 추가해 주자. 그전에 아까 설정했던 롱/숏 포지션 증거금, 피라미딩, 주문 수량, 커미션 등의 기본값도 수정해 주자. 맨 윗줄에서 수정한다.

strategy("ANTS Investment의 BB 전략", overlay=true, 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)

  이번에 종가 기준으로 움직이던 걸 시가, 고가, 저가 or 통상가격이나 다른 가격 평균도 이용할 수 있게 수정해 줄 거다. 가격평균 관련 내용은 아래 포스팅 참조.

2024.03.29 - [기술적 분석] - [기술적 분석] 지표 : 다양한 가격평균의 종류

 

[기술적 분석] 지표 : 다양한 가격평균의 종류

이동평균선에 이어 이번엔 차트 분석이나 보조지표에서 많이 쓰이는 가격평균에 대해 적는다. 우리는 흔히 현재가 혹은 종가 기준으로 가격을 이야기하는데 통상 쓰는 봉차트는 시가(open), 고가

antsinvest.tistory.com

source = close

  원래 위처럼 돼있던 부분을 아래처럼 수정한다.

source = input(close, title="Source")

  이제 전략의 설정을 눌러보자.

  이동 평균 종류도 추가해 보자. 기본은 단순이동평균을 사용 중이다. 이동평균을 다양하게 이용해서 테스트할 수 있게 만들어보자. 이동 평균 종류에 대한 설명은 아래 포스팅을 참조한다.

2024.03.28 - [기술적 분석] - [기술적 분석] 지표 : 다양한 이동평균선의 종류

 

[기술적 분석] 지표 : 다양한 이동평균선의 종류

주가 분석에서 가장 기본적인 보조지표로 사용되는 다양한 이동평균의 종류에 대해 다룬다. 이동평균은 주식에서도 활용되지만 시계열 데이터를 베이스로 하는 통계, 예측 등에서 다양하게 활

antsinvest.tistory.com

basis = ta.sma(source, length)

  위 부분을 아래처럼 수정한다.

maType = input.string("SMA", "Basis MA Type", options = ["SMA", "EMA", "RMA", "WMA", "HMA", "VWMA"])
ma(source, length, _type) =>
    switch _type
        "SMA" => ta.sma(source, length)
        "EMA" => ta.ema(source, length)
        "RMA" => ta.rma(source, length)
        "WMA" => ta.wma(source, length)
        "HMA" => ta.hma(source, length)
        "VWMA" => ta.vwma(source, length)
basis = ma(source, length, maType)

  이번에는 테스트 구간을 설정할 수 있도록 옵션을 추가하겠다. 상승구간을 집중적으로 분석하고 싶을 때, 박스권 구간을 분석하고 싶을 때, 특정 구간을 설정하여 백테스팅 하기 위함이다.

if buyEntry
	strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="매수")
if sellEntry
	strategy.close_all('매도')

  위 부분을 아래처럼 수정한다.

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 buyEntry
		strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="매수")
	if sellEntry
		strategy.close_all('매도')

  이제 수정 사항이 잘 반영되었는지 확인해 보자.

  잘 반영되었다. 이제 전략을 저장 후에 소스, 길이, 곱, 이동평균, 주문금액, 종목 등을 바꿔가며 테스트해 본다. 최적해를 찾는 것은 각자의 몫.

 

  트레이딩 뷰는 보조지표나 보조지표를 활용한 전략을 만들어 손쉽게 백테스팅 해보기 좋은 툴이다. 이런 전략과 보조지표로 얼럿을 생성해 앱에서 푸시 알람을 받아 볼 수도 있다. 잘만 사용한다면 기술적 분석을 이용해 매매를 하는 트레이더들에게 최적의 툴이 아닐까 싶다. 다만, 이렇게 만든 전략들도 종목을 바꿔가며 테스트해봐야 하는데, 이는 유료 결제를 해야 한방에 백테스팅이 되고 무료버전 사용자들은 직접 종목을 바꿔가며 일일이 돌려봐야 한다. 마지막으로 완성된 소스를 공유하며 포스팅을 마친다.

 

 

//@version=5
strategy("ANTS Investmet의 BB 전략", overlay=true, 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)
source = input(close, title="Source")
length = input.int(20, minval=1)
mult = input.float(2.0, minval=0.001, maxval=50)
maType = input.string("SMA", "Basis MA Type", options = ["SMA", "EMA", "RMA", "WMA", "HMA", "VWMA"])
ma(source, length, _type) =>
    switch _type
        "SMA" => ta.sma(source, length)
        "EMA" => ta.ema(source, length)
        "RMA" => ta.rma(source, length)
        "WMA" => ta.wma(source, length)
        "HMA" => ta.hma(source, length)
        "VWMA" => ta.vwma(source, length)
basis = ma(source, length, maType)

dev = mult * ta.stdev(source, length)
upper = basis + dev
lower = basis - dev

plot(basis, color=color.yellow)
bbupper = plot(upper, color=color.blue)
bblower = plot(lower, color=color.blue)
fill(bbupper, bblower, color=color.new(color.blue,80))

slope = (basis[0] - basis[length]) / basis[length]
buyEntry = ta.crossunder(source, lower) or (ta.crossunder(source, basis) and slope > 0)
sellEntry = ta.crossover(source, upper) or (ta.crossover(source, basis) and slope < 0)

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 buyEntry
		strategy.entry("BBandLE", strategy.long, stop=lower, oca_name="BollingerBands", oca_type=strategy.oca.cancel, comment="매수")
	if sellEntry
		strategy.close_all('매도')

bgcolor(strategy.position_size > 0 ? color.new(color.yellow,90) : na)
반응형