본문 바로가기

백테스트 실전

46. VAA 백테스트 (Feat. 트레이딩 뷰)

내가 원하는 대로 동적 자산배분을 자유롭게 백테스트를 할 수 없을까에 대해 찾아보다가 트레이딩 뷰를 알게 되었다.

https://kr.tradingview.com/

 

트레이딩뷰 - 모든 시장을 추적하세요

전세계 차트, 채팅 그리고 트레이드를 할 수 있는 곳. 우리는 트레이더 및 인베스터를 위한 강력한 수퍼-차팅 플랫폼 및 소셜 네트웍입니다. 회원 가입은 무료.

kr.tradingview.com

 

트레이딩뷰에서 회원가입하고 심볼입력에 SPY를 넣으면 미국 주식의 가격을 확인할 수 있다.

 

이제 주식가격을 차트에 띠우고 나면  VAA 전략을 백테스트에 적용하는지 알아봐야하는데 이를 가능하게 해주는 게 바로 pine script 이다. 스크립트에 원하는 조건식을 입력하면 그 결과물을 차트에 그려주는 기능을 구현할 수 있다. 다행히도 유튜브 채널 철투님이 무료로 공개하신 스크립트를 참고하여 분석 및 VAA 백테스트를 해보겠다.

https://www.youtube.com/watch?v=9FsPdvUvj-A 

//@version=4 
study("Vigilant Asset Allocation", "VAA")
 
// * * * 입력 * * * //
// 사용할 공수 자산 입력
offenseSym1 = input("AMEX:SPY", "공격 자산1 (미국 주식)", input.symbol)
offenseSym2 = input("AMEX:EFA", "공격 자산2 (선진국 주식)", input.symbol)
offenseSym3 = input("AMEX:EEM", "공격 자산3 (신흥국 주식)", input.symbol)
offenseSym4 = input("AMEX:AGG", "공격 자산4 (미국 총 채권)", input.symbol)
defenseSym1 = input("AMEX:LQD", "수비 자산1 (미국 회사채)", input.symbol)
defenseSym2 = input("NASDAQ:IEF", "수비 자산2 (미국 중기채)", input.symbol)
defenseSym3 = input("NASDAQ:SHY", "수비 자산3 (미국 단기채)", input.symbol)
 
// 월별 거래 비용
cost        = input(0.0, "편도 거래 비용(%)", input.float)
 
// 백테스트 기간 설정
startYear   = input(1,      "시작 년")
startMonth  = input(1,      "시작 월")
startDay    = input(1,      "시작 일")
endYear     = input(9999,   "종료 년")
endMonth    = input(12,     "종료 월")
endDay      = input(31,     "종료 일")
startPeriod = timestamp(startYear, startMonth, startDay, 0, 0)
endPeriod   = timestamp(endYear, endMonth, endDay, 0, 0)
testPeriod  = time >= startPeriod and time <= endPeriod
 
// 함수 정의 시작
// 종가 가져오기
getClose(sym) => security(sym, timeframe.period, close, barmerge.gaps_off, barmerge.lookahead_on)
 
// 수익률 계산
getYield(src, m) => (src/src[m]-1)*100
 
// 모멘텀 스코어 계산
getScore(src) => 12 * getYield(src, 1) + 4 * getYield(src, 3) + 2 * getYield(src, 6) + 1 * getYield(src, 12)
// 함수 정의 끝
 
// * * * 연산 * * * //
// 공격적인 자산 종가
spy = getClose(offenseSym1)
efa = getClose(offenseSym2)
eem = getClose(offenseSym3)
agg = getClose(offenseSym4)
 
// 수비적인 자산 종가
lqd = getClose(defenseSym1)
ief = getClose(defenseSym2)
shy = getClose(defenseSym3)
 
 
// 공격적인 자산의 모멘텀 스코어
scoreSPY = getScore(spy)
scoreEFA = getScore(efa)
scoreEEM = getScore(eem)
scoreAGG = getScore(agg)
 
// 수비적인 자산의 모멘텀 스코어
scoreLQD = getScore(lqd)
scoreIEF = getScore(ief)
scoreSHY = getScore(shy)
 
// 모든 종목의 모멘텀 스코어가 존재하는가?
isValue = not(na(scoreSPY[1]) or na(scoreEFA[1]) or na(scoreEEM[1]) or na(scoreAGG[1]) or na(scoreLQD[1]) or na(scoreIEF[1]) or na(scoreSHY[1]))
 
// 공격 모드 판별
offenseCondition = not(min(scoreSPY, scoreEFA, scoreEEM, scoreAGG) < 0)
 
// 공격형 자산 중 최고 모멘텀 스코어
scoreOffense = max(scoreSPY, scoreEFA, scoreEEM, scoreAGG)
 
// 수비형 자산 중 최고 모멘텀 스코어
scoreDefense = max(scoreLQD, scoreIEF, scoreSHY)
 
// 공수 자산의 최근 한달간의 수익률(누적 수익률을 구하기 위함)
yieldSPY = getYield(spy, 1)
yieldEFA = getYield(efa, 1)
yieldEEM = getYield(eem, 1)
yieldAGG = getYield(agg, 1)
yieldLQD = getYield(lqd, 1)
yieldIEF = getYield(ief, 1)
yieldSHY = getYield(shy, 1)
 
// 투자 종목별 색상
colorScore = offenseCondition
 ? ((scoreOffense == scoreSPY) ? color.red : 
 (scoreOffense == scoreEFA) ? color.orange :
 (scoreOffense == scoreEEM) ? color.yellow :
 (scoreOffense == scoreAGG) ? color.green : na)
 : ((scoreDefense == scoreLQD) ? color.blue :
 (scoreDefense == scoreIEF) ? color.navy :
 (scoreDefense == scoreSHY) ? color.purple : na)
 
// 월말 투자 종목
nextInvestment = offenseCondition
 ? ((scoreOffense == scoreSPY) ? "미국" : 
 (scoreOffense == scoreEFA) ? "선진국" :
 (scoreOffense == scoreEEM) ? "신흥국" :
 (scoreOffense == scoreAGG) ? "미 총채권" : "")
 : ((scoreDefense == scoreLQD) ? "미 회사채" :
 (scoreDefense == scoreIEF) ? "미 중기채" :
 (scoreDefense == scoreSHY) ? "미 단기채" : "")
  
// 전월과 보유 종목이 같으면 거래 비용 없음
realCost = cost + (nextInvestment[1] == nextInvestment ? 0 : cost) + (nextInvestment[1] == nextInvestment[2] ? - cost : 0)
 
// 공수 전환으로 인해 선택된 자산의 월별 수익률
monthYield = (offenseCondition[1]
 ? ((scoreOffense[1] == scoreSPY[1]) ? yieldSPY : 
 (scoreOffense[1] == scoreEFA[1]) ? yieldEFA :
 (scoreOffense[1] == scoreEEM[1]) ? yieldEEM :
 (scoreOffense[1] == scoreAGG[1]) ? yieldAGG : 0)
 : ((scoreDefense[1] == scoreLQD[1]) ? yieldLQD :
 (scoreDefense[1] == scoreIEF[1]) ? yieldIEF :
 (scoreDefense[1] == scoreSHY[1]) ? yieldSHY : 0)) - realCost
  
// 월별 수익률 누적 결과
var yield = 0.0
yield := testPeriod ? (((1+yield/100) * (1+monthYield/100)) - 1) * 100 : 0.0
 
// 백테스트 기간에만 봉 갯수 세기
var barCount = 0
barCount := barCount + (testPeriod ? 1 : 0)
 
// 연 복리 계산
cagr = (pow(1+yield/100, 1/((barCount)/12))-1)*100
 
// 최고 수익률 계산
var maxYield = 0.0
maxYield := max(maxYield, yield)
 
// 손실폭 계산
var drawdown = 0.0
drawdown := (((1+yield/100)/(1+maxYield/100)) - 1) * 100
 
// 최대 손실폭 계산
var mdd = 0.0
mdd := min(mdd, drawdown)
 
// * * * 출력 * * * //
// 1. 공격 자산의 모멘텀 스코어
plot(scoreSPY, "미국", color.red, 1, plot.style_stepline)
plot(scoreEFA, "선진국", color.orange, 1, plot.style_stepline) 
plot(scoreEEM, "신흥국", color.yellow, 1, plot.style_stepline) 
plot(scoreAGG, "미 총채권", color.green, 1, plot.style_stepline)   
 
// 2. 수비 자산의 모멘텀 스코어
plot(scoreLQD, "미 회사채", color.blue, 1, plot.style_stepline)
plot(scoreIEF, "미 중기채", color.navy, 1, plot.style_stepline)
plot(scoreSHY, "미 단기채", color.purple, 1, plot.style_stepline)
 
// 3. 월별 수익률 & 월말 투자 종목
plot(testPeriod and isValue ? monthYield : na, "월별 수익률(%)", colorScore[1], 1, plot.style_columns)
var labelNext = label.new(bar_index, 0, style = label.style_label_upper_left)
label.set_xy(labelNext, bar_index, 0)
label.set_color(labelNext, colorScore)
label.set_text(labelNext, tostring(month) + "월말 투자 종목 : " + nextInvestment)
 
// 4. 수익률
plot(testPeriod and isValue ? yield : na, "수익률(%)", color.purple)
 
// 5. 연 복리
plot(testPeriod and isValue ? cagr : na, "연 복리(%)", color.green)
 
// 6. 손실폭과 최대 손실폭
plot(testPeriod and isValue ? drawdown : na, "손실폭(%)", color.red)
plot(testPeriod and isValue ? mdd : na, "최대 손실폭(%)", color.orange)

 

해당 스크립트를 실행하면 다음과 같이 복잡한 결과를 볼 수 있는데 밑의 톱니바퀴를 눌러 값을 조절할 수 있다.

 

 

인풋값으로 자산 선택, 거래 수수료, 기간 설정 등등을 할 수 있다. 여기서 시즈널리티를 적용할 수 있겠지?

 

여기서 원하는 지표만 선택적으로 차트에 그릴 수 있다.

위에는 공격자산의 모멘텀 스코어, 밑은 수비자산의 모멘텀 스코어이고 현재 9월 데이터를 보면 모멘텀 스코어가 모두 0 미만이어서 수비자산 3개 중 가장 모멘텀 스코어가 높은 미 단기채를 투자 수단으로 선택한 것을 확인할 수 있다.

 

 

월별 수익률을 체크하면 색깔별로 해당 자산군으로 한달동안 투자했을때의 월별 수익률을 확인할 수 있다.

 

마지막으로 전체 수익률과 연 복리, MDD를 직접 확인할 수 있다. 여기서는 2004년 부터의 데이터로 진행하고 배당수익에 대한 고려가 없어서 공부했던 것과는 조금 차이가 있었다.

 

다음에는 VAA  시즈널리티와 비싼 etf 대신 저렴한 etf로 대체하여 백테스트를 돌려봐야겠다. (새벽 3시네 내일 출근해야지) documentation 공부가 부족해서 빌드 오류를 해결하지 못한다..!

 

+ 추가로 차트에서 이평선 계산할 때 봉 단위로 계산하기 때문에 무조건 전체로 비율을 정해놓고 봐야 한다.

+ 11-4에 전략을 수행하고, 5-10에 전략을 쉬고 SHY로 이동하면 수익이 많이 떨어진다! 어차피 하락장에 대비를 하기 때문에 시즈널리티까지 적용해야 되나 싶다.

'백테스트 실전' 카테고리의 다른 글

47. LAA 백테스트 (feat. 철투)  (0) 2022.09.13