안녕하세요. 아래 3% 손절 로직은 연속적이지 않음으로 손절이 불완전 합니다. 다시말해 두달 연속 3%하락할때는 손절이 발생하지 않습니다. 주식 +현금의 총평가액을 기준으로 비교하기 때문입니다. 이미 지난달에 100%손절 했기 때문에 시장이 한번 더 하락하더라도 총평가액의 변동이 없어서 그렇습니다. 이런 단점을 제거하기 위해 Kospi 지수로 비교하는 로직으로 바꾸었습니다.
지수가 폭락할 때만 현금보유비중의 조정이 발생합니다. 예를들어 한달에 9%혹은 세달에 13% 정도 빠져야 손절이 발생합니다. 이정도면 폭락이라 할만하죠.
아래 주석사이의 STOCK_WEIGHT 부분을 0으로 하면 100% 손절이 발생하고 0.5로 하면 현금보유비중이 50%가 됩니다. 저는 0.5를 추천합니다. 왜냐하면 첫달에 10%빠져도 다음달에 추세가 이어질지 아닐지 누구도 예측할수 없기 때문입니다.
주사위를 10번 던져서 모두 1이 나와도 다음번에 또 1이 나올 확률은 1/6입니다. 주식 시장도 마찬가지 입니다. 한달에 10% 떨어져도 다음달에 반등할 확률은 50% 입니다. 그래서 하락장이라 하더라도 켈리의 법칙인 0.5(50%)를 추천합니다. 하지만 큰 금액을 투자하신다거나 간이 작으신 분들은 100% 손절하세요. 특히 MDD에 민감하신 분들요^^
현금보유비중을 0%으로 하면 수익이 줄어들 수는 있으나 마음은 편합니다. STOCK_WEIGHT 부분을 50%와 0%로 바꿔가면서 비교해 보세요. 아마도 테스트 기간을 2000년 ~ 2017년 으로 하신다면 현금보유비중 0% 보다는 50%가 수익이 좋을 겁니다.
폭락장을 피해가려면 아래 부분을 여러분의 로직에 추가하시면 됩니다.
//현금비중 조절 로직 시작
...
//현금비중 조절 로직 끝
아래는 미국발 폭락장인 리먼브라더스 사태로 테스트한 결과입니다. 테스트 기간은 코스피 지수가 폭락을 시작해서 이전 주가로 완전히 회복하는 2008년 1월초 ~2010년 9월 말로 하였습니다.
kospi 지수는 반토막이 났는데 우리 로직은 2.5% 도 안딸어 졌네요.
두서 없는 글 끝까지 읽어주셔서 감사합니다.
@코딩왕초보 요청하신 로직 드립니다. 점심시간이라 테스트 하지 못하고 보냅니다. 아마 문제 없이 수행될겁니다. var basket0, basket1; //주식 종목들을 담을 Basket var account0, account1; var MAX_SIZE = 20; //각 바스켓에 최대 20주 편입 var STOCK_WEIGHT = 0.99; //주식비중 var basket_num = 0; //관찰대상/손절대상 혹은 진입대상 포트폴리오 구분자. 0 이면 관찰대상, 1 이면 3%손절 및 진입대상 var copyPortfolio; //자바스크립트 수행속도를 빠르게 하기 위해 포트폴리오를 복제함. 이렇게 하지 않고 portfolioBuilder 함수를 두번 CALL하는 사람이 있는데, 두배 느려짐. var currentValue, monthValue1, monthValue2, monthValue3, monthValue4, monthValue5; //관찰대상 포트폴리오의 주식 + 현금 총평가액을 현재월~ 5개월 전까지 저장하기 위한 용도임 // 이 전략이 초기화되면 Initialize 함수가 호출됩니다. function initialize() { IQEnvironment.simulationMethod = SimulationMethod.day; //종가매매로 변경 account0 = IQAccount.getDefaultAccount(); account0.accountName = "관찰대상 포트폴리오"; basket0 = new Basket(account0, MAX_SIZE, IQEnvironment.aum * STOCK_WEIGHT); basket0.setPortfolioBuilder(portfolioBuilder); account1 = IQAccount.addAccount('0000-0000-01', '폭락장 현금비중조절 포트폴리오', IQEnvironment.aum); basket1 = new Basket(account1, MAX_SIZE, IQEnvironment.aum * STOCK_WEIGHT); basket1.setPortfolioBuilder(portfolioBuilder); } //portfolioBuilder 함수에서 사용할 필터링 함수를 정의합니다. function stockFilter(stock) { if (stock.getMarketCapital() === 0) { return false; } if (stock.getFundamentalTotalAsset() === 0) { return false; } if (stock.getClose() === 0) { return false; } // filter out delisted stocks if (stock.getTradingValue() === 0) { return false; } // 거래정지 중인 종목 제외 if (stock.market != 1 || stock.isETF) { return false; } // KOSPI만 & ETF 제외 if (stock.getFundamentalNetProfit() <= 0) { return false; } // 당기 순이익 0 이하 종목 제외 if (stock.getFundamentalTotalEquity <= 0) { return false; } // 당기 자본총계 0 이하 종목 제외 return true; } function portfolioBuilder(targetSize) { switch(basket_num){ case 0 : //관찰대상 포트폴리오 실행로직 var universe = IQStock.filter(stockFilter); var sortedByPbr = universe.slice().sort( function(a, b) { return b.getPBR() - a.getPBR(); }); var sortedByPer = universe.slice().sort( function(a, b) { return b.getPER() - a.getPER(); }); var modelPortfolio = universe.slice().sort( function(a, b) { return b.getScore('rank_sum') - a.getScore('rank_sum'); }); copyPortfolio = modelPortfolio; //현금비중 조절용 basket을 위해 포트폴리오 copy STOCK_WEIGHT = 0.99; currentValue = account0.getTotalEquity(); basket0.setBudget(currentValue * STOCK_WEIGHT); //현금비중 조절 로직위해 global 변수 입력 monthValue5 = monthValue4; monthValue4 = monthValue3; monthValue3 = monthValue2; monthValue2 = monthValue1; monthValue1 = currentValue; break; case 1 : //현금비중조절 포트폴리오 실행로직 if ( currentValue/monthValue1 < 0.91 ||currentValue/monthValue2 < 0.89 || currentValue/monthValue3 < 0.87 || currentValue/monthValue4 < 0.85 || currentValue/monthValue5 < 0.83 ) { STOCK_WEIGHT = 0; } logger.debug('주식비중 :' + STOCK_WEIGHT * 100 + '%' ); basket1.setBudget(account1.getTotalEquity() * STOCK_WEIGHT); break; } return copyPortfolio.slice(0, MAX_SIZE); } var lastRebalMonth = -1; var startDate = 1; //시뮬레이션 기간동안 매일 매일 호출됩니다. //호출되는 시점이 언제인지는 Date 객체인 now파라메터를 통해 알 수 있습니다. function onDayClose(now) { // 매달 초에 리밸런싱을 수행합니다. if ((now.getMonth() != lastRebalMonth && now.getDate() >= startDate)) { basket_num = 0; //관찰대상 포트폴리오(손절없음, 주식비중 항상 99%) : 이 포트폴리오가 3% 하락하면 손절,다시 3% 상승하면 진입함 basket0.setBudget(account0.getTotalEquity() * STOCK_WEIGHT); basket0.buildPortfolio(); basket_num = 1; //현금비중조절 포트폴리오(폭락장에서 현금비중 높아짐) basket1.setBudget(account1.getTotalEquity() * STOCK_WEIGHT); basket1.buildPortfolio(); lastRebalMonth = now.getMonth(); } } 그럼 수고하세요.