인텔리퀀트에서는 여러 종목을 한번에 관리하기 위해 Basket 객체를 제공합니다.
Stock 객체 필터링
대부분의 경우에는 위에서 처럼 직접적으로 종목코드를 통해 가져오지 않고 필터링을 거쳐 원하는 종목들을 가져오게 됩니다. (물론 채권이나 인덱스 ETF같은 경우에는 직접 코드를 통해 가져와야 합니다.)
우선은 종목을 필터링하는 함수를 작성합니다. 이 함수는 stock 객체를 인자로 받아서 true 또는 false를 리턴하는 함수여야 합니다.
우선은 예제를 살펴보겠습니다.
function filterfunc(stock) {
return (stock.getMarketCapital() > 10000); //시가총액이 100억 이상인 종목으로 필터링
}
var universe0 = IQStock.filter( filterfunc ); //필터를 통과한 stock 객체의 배열
for (var i = 0; i < universe0.length; i++) {
var stock = universe0[i];
logger.debug(' code:' + stock.code + ', name:' + stock.name); //각 종목의 코드와 이름을 출력함
}
JavaScript
복사
위 예제에서 filterfunc 함수는 stock을 인자로 받고 시가총액을 기준으로 true또는 false를 리턴합니다. 이런식으로 사용자가 원하는 필터링 함수를 만들어서 특정 조건을 통과한 종목만 가져올 수 있습니다. 시가총액과 20일간 거래 대금 평균이 어느정도 이상인 종목만을 걸러서 가져오거나, PER가 특정 값 이상인 경우에만 선별적으로 가져와서 추가적인 계산을 할 수 있습니다.
Basket을 사용하여 종목을 담고 주문내는 방식이 처음 보면 생소할 수도 있습니다. 그냥 매수할 종목과 매도할 종목을 지정하여 주문을 내주는 것이 매매라는 관점에서는 더 직관적이니까요.
하지만 저희가 제공하는 Basket은 대부분의 자산운용사에서 사용하는 모델 포트폴리오 개념을 그대로 사용할 수 있도록 했습니다. 처음엔 조금 복잡해 보이지만 익숙해 지면 왜 이런 방법을 사용했는지 이해하실 수 있을겁니다.
그리고 중요한 것은 반드시 Basket객체를 사용해야 하는것은 아닙니다. Account객체에 종목을 지정하여 직접 매수/매도 주문을 내도 시뮬레이션은 문제 없이 동작합니다.
Basket에 종목을 담아 포트폴리오를 구성하고 리밸런싱 하는 과정
준비
종목을 선정하는 알고리즘이 실행되는 buildPortfolio 함수를 정의(작성)한다.
초기화
1.
Basket 객체를 생성한다. 이때 Basket에 담을 종목 수와 할당된 투자비중에 맞춰 예산(투자금액)을 지정한다. 또한, 해당 Basket이 어느 계좌에 대한 것인지 Account객체를 지정한다.
2.
Basket 별로 종목 선정을 위해 어떤 buildPortfolio 함수를 사용할 것인지 지정한다.
리밸런싱 때
1.
Basket 별로 정해진 투자비중에 맞춰 예산(투자금액)을 재계산하여 할당한다.
2.
Basket 별로 지정된 buildPortfolio 함수를 호출하여 새로운 목표 포트폴리오를 생성한다.
이렇게 하는 이유는, 여러 자산군 또는 멀티 전략을 이용한 포트폴리오를 구성하려는 경우 여러 개의 개별 포트폴리오를 함께 다루기 쉽게 하기 위해서 입니다.
각각의 포트폴리오를 만드는 방식을 Basket에 정의하고 그 실행결과로 생성되는 목표 포트폴리오를 Basket에 담아 관리함으로써, 현재 보유 내역과 목표 내역의 차이만 실제 매매 대상으로 분리해서 주문 처리하는 것이 포트폴리오 관리라는 측면에서는 오히려 더 직관적이기 때문입니다.
다음은 샘플 코드입니다.
var stock_port;
var short_port;
var stock_weight = 0.8;
var short_weight = 0.2;
function initialize() {
stock_port = new Basket(IQAccount.getDefaultAccount(), 20, IQEnvironment.aum * stock_weight);
short_port = new Basket(IQAccount.getDefaultAccount(), 1, IQEnvironment.aum * short_weight);
stock_port.setPortfolioBuilder(stockPortfolioBuilder);
short_port.setPortfolioBuilder(shortPortfolioBuilder);
IQDate.addRebalSchedule(IQDate.setMonthlyStart(15)); // 리밸런싱 주기를 매월15일로 설정
}
function getPER(stock) {
if (stock.getClose() === 0 || stock.getFundamentalNetProfit() === 0 {
return -1;
}
var per = stock.getMarketCapital() / (stock.getFundamentalNetProfit() * 4);
return 1/per;
}
function filterfunc(stock) {
return (stock.getMarketCapital() > 10000); //시가총액이 100억 이상인 종목으로 필터링
}
function shortPortfolioBuilder(targetSize) {
return [IQStock.getStock("A153130")]; //KODEX 인버스 ETF
}
function stockPortfolioBuilder(targetSize) {
var universe = IQStock.filter( filterfunc );
logger.debug('List_universe size = ' + universe.length);
var sortedByPer = universe.slice().sort(function(a,b){return getPER(b)-getPER(a);});
var port = sortedByPer.slice(0, targetSize);
return port;
}
function onDayClose(now) {
if (IQDate.isRebalancingDay(now)) { // now가 매월 15일 또는 바로 다음 거래일에 true
var totalEquity = IQAccount.getDefaultAccount().getTotalEquity(); // 계좌 총 평가액
stock_port.setBudget(totalEquity * stock_weight);
stock_port.buildPortfolio();
logger.debug('cash = ' + IQAccount.getDefaultAccount().cash + ', OPEN VALUE = ' + stock_port.getEquityOpenValue());
}
}
JavaScript
복사
위에서 보시는 것처럼 onDayClose 함수에서 buildPortfolio 함수가 호출되면, 초기화(initialize) 때 Basket에 등록한 portfolioBuilder 함수가 호출되어 그 함수에서 정해 놓은 알고리즘에 따라 종목들을 선정하고, 이 종목 목록을 배열로 리턴하여 Basket의 목표 포트폴리오를 관리하는 변수에 저장합니다.
그럼 새롭게 구성된 목표 포트폴리오는 언제 매매되어 해당 계좌(Account객체)에 실제로 담겨질까요?
리밸런싱이 발생한 날짜에 onDayClose 함수의 절차를 모두 마치고 나면 시뮬레이션 상 매매 가격에 대한 가정 (IQEnvironment.simulationMethod)에 따라,
•
‘당일 종가’인 경우에는 바로 매매 처리를 실행하여, 시뮬레이션 날짜가 다음 날로 넘어 가기 전에 계좌에 종목이 변경되어 목표 포트폴리오 내역대로 담기게 됩니다.
•
‘익일 시가’ 또는 ‘익일 평균가’인 경우에는, 일단 시뮬레이션 날짜가 하루 더 진행되어 그 날짜로 onDayClose 함수가 호출되기 직전에, 그 날의 거래 가격을 이용하여 매매 처리를 실행함으로써 목표 포트폴리오 내역대로 종목이 계좌에 담기도록 합니다.