(Python을 이용한 Quant 투자) VAA 백테스트 : using trandingView

Posted by : at

Category : Python



  • VAA란?
    • 공격자산, 수비자산으로 나누고 최근 실적에 따라 공격 or 수비자산으로 자산을 투자하는 전략
      • 공격자산 : SPY(미국 주식), VEA(선진국 주식), VWO(개발도상국 주식), AGG(미국 잡채권)
      • 수비자산 : SHY(미국 단기국채), IEF(미국 중기국채), LQD(미국 회사채)
    • 상대모멘텀 : 4개 공격 자산 중 최근 잘 나가는 1개를 산다
      • 단, 4개 공격 자산 중 하나라도 마이너스라면 수비로 전환
    • 최근의 정의는 (12 * 1개월) + (4 * 3개월) + (2 * 6개월) + (1 * 12개월) 수익

  • 백테스팅은 tradingview를 통해 진행
    • Pine 에디터에서 코딩가능

우선 다른 종목들을 불러와 보자

// ...

//@version=4
study("내 스크립트")
spy = security("SPY", timeframe.period, close)
// 티커 : SPY, 현재 그래프의 전체시간을 : timeframe.period, 종가기준으로 : close
plot(spy)
// 출력해달라

참고) 함수가 궁금할때 Pine 스크립트 언어 레퍼런스를 참조

  • spy의 모멘텀 스코어를 구해보자
    • 모멘텀 스코어 : (당월 종가 / 전월 종가)
    • 당월 종가가 더 높다면 1초과 / 전월 종가가 높다면 1미만 출력될 것
  • 우선 1개월 단위 수익률을 계산해 보자
    • ([당월 종가] / [전월 종가] - 1) * 100
// ...

//@version=4
study("내 스크립트")
spy = security("SPY", timeframe.period, close)

y = (spy/spy[1]-1)*100
// y = (spy/spy[2]-1)*100 // 2개월 수익률
// y = (spy/spy[6]-1)*100 // 6개월 수익률

/*
    spy : 당월 종가
    spy[1] : 1개월 전 종가
    spy[6] : 6개월 전 종가
*/
plot(y)

  • 우리가 사용할 데이터는 1, 3, 6, 12 개월의 수익률이니
// ...

//@version=4
study("내 스크립트")
spy = security("SPY", timeframe.period, close)

// X개월 수익률을 구하는 함수를 만들어 보자
getYield(m) => (spy/spy[m]-1)*100   // m is month
y1 = getYield(1)
y3 = getYield(3)
y6 = getYield(6)
y12 = getYield(12)

plot(y1, "y1", color.red)
plot(y3, "y3", color.orange)
plot(y6, "y6", color.green)
plot(y12, "y12", color.blue)

  • 모멘터 스코어의 점수를 구해보자
// ...

//@version=4
study("내 스크립트")
spy = security("SPY", timeframe.period, close)

getYield(m) => (spy/spy[m]-1)*100

score = 12 * getYield(1) + 4 * getYield(3) + 2 * getYield(6) + 1 * getYield(12)

plot(score, "score", color.red)


  • 여기까지 spy의 스코어를 계산, 나머지 자산에 대하 스코어를 계산해보자
// ...

//@version=4
study("VAA")
// 공격
spy = security("AMEX:SPY", timeframe.period, close)
efa = security("AMEX:EFA", timeframe.period, close)
eem = security("AMEX:EEM", timeframe.period, close)
agg = security("AMEX:AGG", timeframe.period, close)

// 수비
lqd = security("AMEX:LQD", timeframe.period, close)
ief = security("NASDAQ:IEF", timeframe.period, close)
shy = security("NASDAQ:SHY", timeframe.period, close)


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)


// 공격
scoreSPY = getScore(spy)
scoreEFA = getScore(efa)
scoreEEM = getScore(eem)
scoreAGG = getScore(agg)

// 수비
scoreLQD = getScore(lqd)
scoreIEF = getScore(ief)
scoreSHY = getScore(shy)

plot(scoreSPY, "scoreSPY", color.red)
plot(scoreEFA, "scoreEFA", color.red)
plot(scoreEEM, "scoreEEM", color.red)
plot(scoreAGG, "scoreAGG", color.red)

plot(scoreLQD, "scoreLQD", color.blue)
plot(scoreIEF, "scoreIEF", color.blue)
plot(scoreSHY, "scoreSHY", color.blue)


백테스팅

// ...

//@version=4
study("각 공격형 자산의 모멘텀 스코어가 가장 높을때 월별 수익률")
// 공격
spy = security("AMEX:SPY", timeframe.period, close)
efa = security("AMEX:EFA", timeframe.period, close)
eem = security("AMEX:EEM", timeframe.period, close)
agg = security("AMEX:AGG", timeframe.period, close)

// 수비
lqd = security("AMEX:LQD", timeframe.period, close)
ief = security("NASDAQ:IEF", timeframe.period, close)
shy = security("NASDAQ:SHY", timeframe.period, close)


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)


// 공격
scoreSPY = getScore(spy)
scoreEFA = getScore(efa)
scoreEEM = getScore(eem)
scoreAGG = getScore(agg)

// 수비
scoreLQD = getScore(lqd)
scoreIEF = getScore(ief)
scoreSHY = getScore(shy)

// 공격 자산중 가장 작은 값이 0보다 작은지 확인
    // 작을 경우 방어자산으로...
offenseCondition = not(min(scoreSPY, scoreEFA, scoreEEM, scoreAGG) < 0)
// defenseCondition = min(scoreLQD, scoreIEF, scoreSHY) < 0

// 공격형 자산의 점수
scoreOffense = max(scoreSPY, scoreEFA, scoreEEM, scoreAGG)
// 수비형 자산의 점수
scoreDefense = max(scoreLQD, scoreIEF, scoreSHY)

rangeSPY = getYield(spy, 1)
rangeEFA = getYield(efa, 1)
rangeEEM = getYield(eem, 1)
rangeAGG = getYield(agg, 1)

range = (scoreOffense[1] == scoreSPY[1]) ? rangeSPY : (scoreOffense[1] == scoreEFA[1]) ? rangeEFA : (scoreOffense[1] == scoreEEM[1]) ? rangeEEM : rangeAGG

plot(range, "range", color.red)

// ...

//@version=4
study("각 공격형 자산의 모멘텀 스코어가 가장 높을때 월별 수익률")
// 공격
spy = security("AMEX:SPY", timeframe.period, close)
efa = security("AMEX:EFA", timeframe.period, close)
eem = security("AMEX:EEM", timeframe.period, close)
agg = security("AMEX:AGG", timeframe.period, close)

// 수비
lqd = security("AMEX:LQD", timeframe.period, close)
ief = security("NASDAQ:IEF", timeframe.period, close)
shy = security("NASDAQ:SHY", timeframe.period, close)


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)


// 공격
scoreSPY = getScore(spy)
scoreEFA = getScore(efa)
scoreEEM = getScore(eem)
scoreAGG = getScore(agg)

// 수비
scoreLQD = getScore(lqd)
scoreIEF = getScore(ief)
scoreSHY = getScore(shy)

offenseCondition = not(min(scoreSPY, scoreEFA, scoreEEM, scoreAGG) < 0)
defenseCondition = min(scoreLQD, scoreIEF, scoreSHY) < 0

scoreOffense = max(scoreSPY, scoreEFA, scoreEEM, scoreAGG)
scoreDefense = max(scoreLQD, scoreIEF, scoreSHY)

rangeSPY = getYield(spy, 1)
rangeEFA = getYield(efa, 1)
rangeEEM = getYield(eem, 1)
rangeAGG = getYield(agg, 1)

rangeLQD = getYield(lqd, 1)
rangeIEF = getYield(ief, 1)
rangeSHY = getYield(shy, 1)

// 공격일때 공격자산 넣고 아닐때 0넣게 수정
range = offenseCondition 
  ? ( (scoreOffense[1] == scoreSPY[1])
  ? rangeSPY : (scoreOffense[1] == scoreEFA[1]) 
  ? rangeEFA : (scoreOffense[1] == scoreEEM[1]) 
  ? rangeEEM : (scoreOffense[1] == scoreAGG[1]) 
  ? rangeAGG : 0 ) : 0

// 공격일때 공격자산 넣고 아닐때 수비형자산 넣게 수정
range2 = offenseCondition 
 ? ( (scoreOffense[1] == scoreSPY[1]) 
 ? rangeSPY : (scoreOffense[1] == scoreEFA[1]) 
 ? rangeEFA : (scoreOffense[1] == scoreEEM[1]) 
 ? rangeEEM : rangeAGG ) 
 : ( (scoreDefense[1] == scoreLQD[1]) 
 ? rangeLQD : (scoreDefense[1] == scoreIEF[1]) 
 ? rangeIEF : rangeSHY )

plot(range2, "range", color.red)
  • Pine 스크립트에서 줄바꿈을 하고 싶을경우
// 들여쓰기(스페이스바) 주의
var = condition 
 ? a
 : b

누적수익률 계산하기

// ...

//@version=4
study("각 공격형 자산의 모멘텀 스코어가 가장 높을때 월별 수익률")
// 공격
spy = security("AMEX:SPY", timeframe.period, close)
efa = security("AMEX:EFA", timeframe.period, close)
eem = security("AMEX:EEM", timeframe.period, close)
agg = security("AMEX:AGG", timeframe.period, close)

// 수비
lqd = security("AMEX:LQD", timeframe.period, close)
ief = security("NASDAQ:IEF", timeframe.period, close)
shy = security("NASDAQ:SHY", timeframe.period, close)


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)


// 공격
scoreSPY = getScore(spy)
scoreEFA = getScore(efa)
scoreEEM = getScore(eem)
scoreAGG = getScore(agg)

// 수비
scoreLQD = getScore(lqd)
scoreIEF = getScore(ief)
scoreSHY = getScore(shy)

offenseCondition = not(min(scoreSPY, scoreEFA, scoreEEM, scoreAGG) < 0)
defenseCondition = min(scoreLQD, scoreIEF, scoreSHY) < 0

scoreOffense = max(scoreSPY, scoreEFA, scoreEEM, scoreAGG)
scoreDefense = max(scoreLQD, scoreIEF, scoreSHY)

rangeSPY = getYield(spy, 1)
rangeEFA = getYield(efa, 1)
rangeEEM = getYield(eem, 1)
rangeAGG = getYield(agg, 1)

rangeLQD = getYield(lqd, 1)
rangeIEF = getYield(ief, 1)
rangeSHY = getYield(shy, 1)

range = offenseCondition 
  ? ( (scoreOffense[1] == scoreSPY[1])
  ? rangeSPY : (scoreOffense[1] == scoreEFA[1]) 
  ? rangeEFA : (scoreOffense[1] == scoreEEM[1]) 
  ? rangeEEM : (scoreOffense[1] == scoreAGG[1]) 
  ? rangeAGG : 0 ) : 0

range2 = offenseCondition 
 ? ( (scoreOffense[1] == scoreSPY[1]) 
 ? rangeSPY : (scoreOffense[1] == scoreEFA[1]) 
 ? rangeEFA : (scoreOffense[1] == scoreEEM[1]) 
 ? rangeEEM : rangeAGG ) 
 : ( (scoreDefense[1] == scoreLQD[1]) 
 ? rangeLQD : (scoreDefense[1] == scoreIEF[1]) 
 ? rangeIEF : rangeSHY )

var accRange = 0.0 // var = static
// 누적수익률
accRange := ((1 + accRange / 100) * (1 + range / 100) - 1) * 100

plot(accRange, "accRange", color.red)


About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links