커뮤니티
내가 만든 전략들과 지식을 공유하고 토론합니다.

슈퍼밸류 모멘텀 전략 1.0 벤치마크 테스트와 Javascript 최적화

소포클레스 2017.09.15 01:58 조회수  2440 추천 5

2017.09.16 로직에 오류가 있습니다. 아래. 코딩왕초보님 댓글을 보시고 수정해서 사용하시기 바랍니다. 코딩왕초보님 오류 제보 감사합니다. 구체적으로는 108라인의 PBR을 PSR로 수정하시면 됩니다


최근에 강국환님께서 집필하신 책이 나왔습니다.

그중에 수익률이 가장높은  슈퍼밸류 모멘텀 전략 1.0 을 최적화 해보았습니다. 


먼저 책에 있는 로직은 "슈퍼밸류 모멘텀 1.0 (50종목)_Original" 입니다. 

이 전략의 핵심은 시가총액 기준 하위 20%의 소형주 중에서, PBR + PER + PSR + GP/A 로 순위를 정해서 상위 50 종목을 선택하고, 50종목중에 12개월 절대모멘텀이 + 인 종목으로 필터링해서  매월 리발란싱 하는 것입니다.  그런데 책의 로직은 종목의 수가 어느 정도 이상 안되면 현금을 보유한다고 합니다. 책 내용은 50종목 중에 1/4 이 안되면 현금보유 100%라고 되어 있네요.


그런데 다음페이지에는 "최소 11종목을 보유할 수 있다" 고 되어있네요. 그래서 현금보유 부분의 로직은 11 종목이 안되면
현금을 보유하는 것으로 했습니다. 


"슈퍼밸류 모멘텀 1.0 (50종목)_Original"과 비교되는 포트폴리오가 5개 입니다.

5종목, 10종목 ,20종목, 30종목, 50종목으로 Original 버전과 비교해보니 모두 좋아졌습니다.

심지어 손절 로직이 있는 Original 버전  50종목과 항상 주식비중 100%인 50종목을 비교해보아도

MDD값이 차이나지 않습니다. 차이는 없는데 수익률만 Original 버전이 떨어집니다.


왜냐하면 절대모멘텀은 하락장에서 힘을 발휘함으로 어느정도 방어가 됩니다. 그래서 인위적으로 손절을 하는 것이
좋지 않다고 생각되네요. 물론 MDD가 줄어든다면 의미가 있겠지만요. 그래서 비교 로직 5개는 현금보유로직이 없습니다.


슈퍼밸류 모멘텀 전략 1.0의 벤치마크 테스트 결과는? 

보유 종목수가 작아질 수록 수익이 증가합니다. 최종승자는 보유종목 10개입니다. 2위는 보유종목 5개 입니다.

1위는 수익률이 2백만%가 넘네요. 쓸만한 것같습니다.


책에는 보유종목수가 작으면 몰빵이라서 위험하다고 되어있습니다. 하지만 10종목이면 심하게 작은 것은 아니죠. 

상장폐지 될수 있다고 하는데요. 그럴 수도 있겠지만 저는 가능성을 그렇게 높게 보지 않습니다. 


이미 PBR + PER + PSR + GP/A (VALUE와 QUALITY)가 소형주 중에 가장 우수한 50개 종목을 선정했기 때문입니다. 

가장 우수한 종목들이 상장폐지 된다면 그보다 열등한 종목들은 모두 파산해야 되겠죠? 오히려 책처럼 50종목을

선정하는 것 보다 VALUE와 QUALITY 최상위 10종목을 선정하는 것이 더 안전해 보입니다. 


상장폐지 측면에서 그렇다는 거지요 .물론 수익률 그래프의 파동은 50종목 보다 10종목이 불안정 합니다.

그렇다고 2백만% 수익률을 포기하고 3만% 수익률을 선택하자니 고민될 겁니다. 공격적인 투자자 뿐만 아니라

보수적인 투자자도 이 부분은 고민을 하셔야 됩니다.


Java Script 초보의 로직 튜닝

이제 책 이야기는 그만하고 로직 튜닝 이야기를 하겠습니다. (제직업이 튜너라 ^^) 슈퍼밸류 모멘텀 전략 1.0을 튜닝하는 것이 아니라 

Javascript를 최적화하는 것 입니다. 여러가지 케이스를 비교할 때 보통은 PortfolioBuilder 함수를 여러개 만들어서 

사용하더군요. 이거 않좋습니다. 그렇게 하면 서버에 부하만 주고 성능도 안나옵니다. 물론 서버에 부하를 몇배로 주어서

빨리 나온다면야 그렇게 하는게 맞지만, 더 느려지기 때문에 장점은 없고 단점만 있습니다. 


아래 로직처럼 PortfolioBuilder 를 하나만 만들고 공유하는 것이 나아보입니다. 대충 백테스트 시간을 계산해보니 많이 빨라졌네요.

DB 서버나 WAS에 부하가 다섯배 정도 줄어들것이고,   수행시간은 대충 5배 빨라졌습니다. 이제 좀 쓸만한 프로그램이 되었습니다. 

그뿐이 아닙니다. 동시에 여러명이 테스트 하는 경우 내가 서버에 부하를 덜주면 그만큼 다른 사람들이 돌린 프로그램이 빨리 끝날것 입니다.   


제가 자바스크립트 초보라 튜닝하기가 힘드네요. 그리고 제가 바빠서 저번에 가치가중 포트폴리오 구현하기로 하고선 아직도 못하고 있네요ㅠㅠ 

두서없는 글 끝까지 읽어주셔서 감사합니다. 


PS

Prohibit님 께서 수익률 200만%에 혹해서 테스트도 안해보고 덤비는 사람들이 있을까봐 좋은 의견을 남겨주셨습니다. 모든 상황에서 10종목의 수익률이 좋은 것은 아니니, 반드시 백테스트 해보시고 로직에 약한 부분은 없는지 검증한 후에 투자하셔야 합니다. 그리고 소형주이기 때문에 많은 금액으로 투자하면 슬리피지도 높아집니다.

Prohibit님 좋은 의견 감사드립니다.

댓글 24
정성글엔 추천!. 
재무 데이터가 없는 경우에 -1을 리턴하시는데 아예 제외시키는게 노이즈가 없지 않을까요?

그리고 예전에 팩터 6가지를 가지고 조합별로 테스트해봤었는데
(코스닥 시총 데이터 오류때문에 다시 해야하지만 ㅠㅠ)
팩터조합, 포트종목수, 손절종목수, 모멘텀 별로 수익률이 다 달라서 
무조건 10종목이 우수하다고는 얘기할 수 없으니 
원하는 팩터 조합이 있다면 반드시 백테스트 해보는 것을 권해드립니다
Prophit 2017.09.15 09:23
감사드립니다. 많은 도움이 되었습니다^^
Leo Hwang 2017.09.15 09:29
@Leo Hwang 
감사합니다
소포클레스 2017.09.15 09:59
@Prophit 
안녕하세요.
재무데이터가 -1인건들은 stockfilter에서 걸러내게 되어 있습니다.
무조건 10종목이 우수하다는 글이 어느부분에 있나요? 제가 주장하는 것은 "value와 quality가 우수한 상위10건 중 상장폐지가 나올 확률은 value와 quality가 우수한 상위50건중 상장폐지가 나올확률보다 낮다" 입니다. 그리고 기관투자자가 아니라면 매월 50종목 투자한다는 것이 현실적으로 가능하지 않습니다. 개미들 입장에서 월 10종목이 현실적인 것입니다. 
마지막으로 "원하는 팩터 조합이 있다면 반드시 백테스트 해보는 것을 권해드립니다" 라고 하셨네요. 너무도 당연한 말씀입니다. 이글은 50가지 백테스한 결과중 하나일 뿐입니다.
소포클레스 2017.09.15 10:00
죄송합니다.
다시 읽어보니 대놓고 오해를 부르는 댓글을 달았네요.

소포클레스 님께 드리는 말이 절대아니고 
혹시나 이 글을 읽는 저같이 퀀트 처음 접하시는 분들을 위해 드리는 말이었습니다.
"수익률이 이렇게 했을때 제일 잘나왔으니 무조건 이거 써야지" 라는 생각을 저도 했었거든요.

가르치려드는 것같아 기분나쁘셨다면 정말 죄송합니다.
Prophit 2017.09.15 10:16
네 프로히빗님의 좋은 의견은 글에 반영하겠습니다. 
소포클레스 2017.09.15 10:17
@소포클래스
의견 감사드립니다. 저희 서버를 아껴주셔서 더더욱 감사드리구요. ^^
그런데 사실과 너무 다른 부분이 있어 정정해 드려야 할 내용이 있습니다.
PortfolioBuilder 를 하나로 만든다고 해서 성능에 큰 영향은 없습니다.
자바스크립트 언어 특성상 하나의 함수를 호출하나 여러 함수를 호출하나 호출되는 횟수가 동일하다면 수행 시간은 비슷합니다.  이 알고리즘의 경우에는 onDayClose 가 한번 호출 될때 마다 6번이 호출되고 있네요. 6개의 각각 다른 함수를 한번씩 호출하거나, 하나의 함수를 6번 호출하거나 어쨌든 6번 호출은 수행시간 측면에서는 비슷합니다.
물론 함수하나가 차지하는 메모리 크기가 있지만 전체적으로 보면 미미한 정도입니다.

그래도 슈퍼밸류 모멘텀이라는 멋진 전략을 공유해 주셔서 저희도 도움이 되었습니다.
계속 의견 올려주시면 좋겠습니다.

P.S. 근데 튜너라는 직업이 있나요?
등엔트로피 2017.09.15 13:56
좋은 자료 감사드립니다!! 
정윤 2017.09.15 14:27
@정윤
도움이 되었다면 다행입니다
소포클레스 2017.09.15 15:23
@등엔트로피  
코드를 자세히 안보신것 같습니다^^ 로직을 자세히 보시면 PortfolioBuilder 함수를 호출하는 것은 6번 이지만 실제로 실행하는 것은 한번 입니다. 첫번째 Basket만 실행하고 나머지 경우는 딱한번 50종목 Original인 경우 setBudget 함수만 실행됩니다. 그러니 DB쪽이나 WAS 쪽 부하가 5배정도 줄어드는 게 맞습니다. 함수를 6개 만든것과 비교해보시면 됩니다^^ 이것은 코딩량이 줄어 들뿐만아니라 백테스트 성능도 향상되고 서버부하도 감소하니 1석 3조입니다. 물론 각 시나리오의 로직이 완전히 다르다면 따로 만드는게 좋겠죠. 
그리고 저의 직업이 DBMS 튜너입니다. 실제로 튜너가 있습니다^^
소포클레스 2017.09.15 16:08
@소포클래스
IT전문가 셨군요.

그러니까 말씀하신 튜닝 방법은 porfolioBuilder 함수를 여러개 쓰는게 문제가 아니고
그 함수 안에 불필요한 코드가 들어있어서 그게 자주 실행되는게 문제라는 의미셨군요.
위 본문 내용은 함수 하나만 만들어 공유하라고 하셔서 제가 그렇게 이해를 했습니다.
실제로 function call은 6번 일어나거든요. 
그 함수 안에서 실행되지 않도록 if 문으로 분기처리해서 부하를 줄인건 맞습니다.


그렇다면 말씀하신 부하를 줄이고, 전역변수를 사용하지 않게 아래처럼 구현하면 좀더 깔끔할것 같습니다.

function builder_long(targetSize) {
	//sort 도 하고 복잡한 로직
}

function builder_short(targetSize) {
	//단순한 로직
}

funtion onInit() {
    Basket1.setPortfolioBuilder(builder_long);
    Basket5.setPortfolioBuilder(builder_short);      
    Basket10.setPortfolioBuilder(builder_short);           
    Basket20.setPortfolioBuilder(builder_short);  
    Basket30.setPortfolioBuilder(builder_short);                
    Basket50.setPortfolioBuilder(builder_short);
}

funtion onDayClose(now) {
	Basket1.setBudget(account1.getTotalEquity() * STOCK_WEIGHT);
        Basket1.buildPortfolio();
        Basket5.setBudget(account5.getTotalEquity() * STOCK_WEIGHT);
        Basket5.buildPortfolio();
        //나머지 반복...      
}

P.S. 
저희 DB와 WAS 부하는 비밀이라 몇 배가 개선되는지는 알려드릴 수 없습니다. 
5배정도 줄어든다고 말씀하셔서 해킹하셨는지 알고 전체 보안점검까지 할뻔 했네요. ^^;;
등엔트로피 2017.09.15 17:01
@등엔트로피 
좋은 방법을 보내주셨네요. 감사드립니다. 보내주신 내용들이 튜닝입니다. 튜너가 따로 있는 것이 아니고 등엔트로피님도 튜너입니다.   
ps
ㅎㅎ 저는 튜너이지 해커는 아닙니다. 5배 개선된다고 한것은 6번 실행할 것을 1번만 실행하니 수행속도가 실제로 5배 빨라졌습니다. 그래서 5배 말씀을 드린겁니다.   
소포클레스 2017.09.15 17:08
@등엔트로피
그런데 질문있습니다. 저렇게 short, long 함수를 따로만들어서 좋아지는 게 있나요? 저렇게 해도 global 변수를 써야할 것 같습니다만...
저도 자바스크립트 초보라 배워야 되니.  short, long 함수로 튜닝된 스크립트를 올려주실 수 있으신지요?
감사드립니다. 
소포클레스 2017.09.15 17:16
흥미진진한 토론에 살짝 끼어 들어 보겠습니다. ^^
소포클레스님의 튜닝의 핵심은 momentumPort를 global 변수로 썼다는 점인 것 같습니다.
그 전제 하에서는 함수를 하나로 만들던, 두개로 쪼개든 효율성은 별다르지 않을 것 같네요.
참.. Tip 하나 드리자면, portfolioBuilder 함수에서 return 하는 배열의 크기가 아무리 커도, 내부적으로 해당 basket 사이즈 만큼만 취하도록 되어 있어서, 아마도 basket_num은 굳이 global 변수로 선언해서 사용하지 않아도 될 듯 합니다. 
Joseph 2017.09.15 17:32
@소포클래스
위에 Joseph님이 이야기하신 내용과 같은 의견이라 세부적인 내용은 burrow(?) 하겠습니다. 

제가 첫 댓글을 달았던 이유는 다른 분들이 실제코드를 통해 소포클래스님의 의도를 이해하지 않고 본문 내용만으로 portfolioBuilder를 하나만 구현해야 하나보다 라고 오해하지 않도록 하기 위해서 였습니다. 그러면 코드 작성하기가 너무 어려워 질거 같아서요. 저희는 자바스크립트를 어떻게 쓰면 효율적인지 구체적으로 가이드하는게 목표가 아니라 자바스크립트를 모르는 분들도 ctrl-C, V만으로 전략을 만들수 있는 템플릿을 많이 제공해드리는게 목표입니다. 그래서 댓글에서 뼈대코드를 올린거구요.

사실 템플릿보다 더 중요한건 이런 감동적인 전략입니다.
앞으로도 많이 올려주시면 저희도 더 나은 전략 개발 환경을 만들어 보겠습니다.
등엔트로피 2017.09.15 17:44
조셉님 팁 감사드립니다. 자바스크립트 내공이 올라갔습니다.
소포클레스 2017.09.15 17:56
등엔트로피님 의견 잘 알았습니다. 앞으로 자바스크립트 튜닝보다는 좋은 매매전략을 올리겠습니다. 그리고 템플릿을 공격할 의도는 아니었습니다. 기분나쁘셨다면 사과드립니다
소포클레스 2017.09.15 17:58
@소포클래스
*^^*
등엔트로피 2017.09.15 18:06
댓글을 읽다보니 세상엔 훌륭하신 분들이 너무 많으시네요.ㅎㅎ
전혀 무슨말인지 이해 못하고 읽었습니다.ㅠㅠ
melodica7 2017.09.15 18:56
@melodica7
오랜만에 뵙습니다^^
소포클레스 2017.09.15 20:09
좋은글 감사드립니다~~
로직에 오류가 있는거같아 문의드립니다. ㅎ PSR정렬에서
var sortedByPSR  = universe.slice().sort( function(a, b) { return PSR(b) - PBR(a); });
PBR로되어있네욤~~
코딩왕초보 2017.09.16 15:07
그러네요. 감사합니다. 글에 반영하겠습니다 
소포클레스 2017.09.16 15:20
제가 코딩 초보라서 그런데 지금 이 코딩대로 투자를 하면, 예를 들어 20종목 portfolio에서 5종목이 12개월 절대모멘텀이 음수가 나와서 포트폴리오에서 제외된다면, 전체 자금의 5/20은 현금보유를 하는 건가요? 아니면
 전체 자산을 15종목에 공동 배분하는 건가요? 코드를 보다보면 모멘텀필터를 적용하고 남은 종목 수에 동일자산배분하는 것처럼 보여서 여쭤봅니다...(실은 제가 코드를 못읽습니다...)
BuSeon Jeong 2017.09.17 21:53
@BuSeon Jeong
원래 포트폴리오가 20종목이고 절대 모멘텀 때문에 5종목만 남았다면 5종목에 현금을 모두 투자합니다. 현금을 남기지 않습니다. 
소포클레스 2017.09.18 09:45
댓글 등록을 위해서 로그인해주세요.
 
최신 게시글