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

마켓타이밍을 적용해 리밸런싱일 외에 진입/청산을 했을때 바스켓 내용이 바뀌지 않게 하려면 어떻게 하면 될까요?

슈퍼공돌맨 2021.09.30 16:33 조회수  181 추천 0

Knuth님의 글 (https://www.intelliquant.co.kr/article/870?forum=8)을 보고 인상깊어서 여러모로 만져보던 와중 막힌부분이 있어 질문드립니다.

(Knuth님 코드는 편의상 PER지표와 마켓타이밍 부분 빼고 지웠습니다. )


분기별 데이터가 업데이트 될때 (4 6 9 12월 첫째날) PER로 종목을 선택하고, 코스닥이 3,5,10일 이평선을 동시하회할때 도망가는 알고리즘입니다. 

재진입은 코스닥 종가가 3,5,10일 이평선 중 하나라도 상회하면 돌아옵니다. 


지금 해결하고자 하는것은 다음과 같습니다.

지금 조건에선 4,6,9,12월 첫째날이 아니더라도 도망갔다가 다시 들어올때 stock_basket.buildPortfolio() 가 수행되고,

다시 돌아오는 시점의 가격기준으로 다시 바스켓이 구성되는지라 리밸런싱일에 고른 종목과 다른녀석들이 바스켓에 들어옵니다.

(가령 4월1일에 A B C D E를 선택했지만 중간에 도망갔다가 4월 15일에 돌아왔다면 그때 기준으로 재정렬해 A B C F G로 종목이 바뀌는...) 


리밸런싱일에 A B C D E를 골랐다면 나중에 도망갔다가 다시 들어올때도 A B C D E를 사게 하고싶습니다.

도움말을 보면서 조금씩 수정해봤습니다만 잘 안되서 여러분들의 지혜를 구합니다.


var stock_basket;              

var stock_num = 5;             

var stock_weight = 0.96;        // 주식 비중 (거래비용 고려 현금 4% 확보)

var draw_down_flag = false;

var currentTotalEquity;

var isFirst = true;             // 시뮬레이션 시작일에 바로 포트폴리오 신규 구성을 하기 위해 사용될 상태 변수

// 초기화

function initialize() {

    stock_basket = new Basket(IQAccount.getDefaultAccount(), stock_num, IQEnvironment.aum * stock_weight);

    stock_basket.setPortfolioBuilder(stockPortfolioBuilder);    

    IQAccount.getDefaultAccount().accountName = "마켓타이밍"

    IQDate.addRebalSchedule(IQDate.setYearly(6, 1));  // 1분기 재무재표 리밸런싱

IQDate.addRebalSchedule(IQDate.setYearly(9, 1));  // 2분기 재무재표 리밸런싱

    IQDate.addRebalSchedule(IQDate.setYearly(12, 1)); // 3분기 재무재표 리밸런싱

    IQDate.addRebalSchedule(IQDate.setYearly(4, 1));  // 4분기 재무재표 리밸런싱

}

/// 평가지표 블록

// PER 

function per(stock) { 

    if (stock.getFundamentalProfit_ownersOfParent() === 0) return -1;

    if (stock.getFundamentalNetProfit() === 0) return -1;

    var cap = stock.getMarketCapital(0) * 1000

    return cap/(stock.getFundamentalProfit_ownersOfParent() * 4)  ;

}


// 상승 하락장 판단문

function KOSDAQ_filter() {

    var kosdaq = IQIndex.getIndex('301');

    var MA_3 = kosdaq.getMA(3);

var MA_5 = kosdaq.getMA(5);

var MA_10 = kosdaq.getMA(10);

    var current_price = kosdaq.getClose();

    if ((current_price < MA_3 && current_price < MA_5 && current_price < MA_10 )) {

         stock_weight = 0;

         stock_basket.reset();

         draw_down_flag = true;

         logger.debug("Out");

    } else {

      stock_weight = 0.96;

if (draw_down_flag) {

         currentTotalEquity = IQAccount.getDefaultAccount().getTotalEquity();

         stock_basket.setBudget(currentTotalEquity * stock_weight);

         stock_basket.buildPortfolio();

         draw_down_flag = false;

         logger.debug("In " + stock_weight);} 

    }

}

// 3) 필터링 함수 정의 - 필터링 조건에 따라 종목들의 포함 여부 판단

function stockFilter(stock) {

    if (stock.isETF) return false;

  return true;

}

// 4) 포트폴리오 빌더 함수 정의 - 필터링 및 팩터 기반 종목 선정

function stockPortfolioBuilder(targetSize) {

    var universe = IQStock.filter(stockFilter);

    var sortedByPer = universe.slice().sort(function(b,a){return per(a) - per(b);}); 


    universe.forEach( function(stock) {

        stock.setScore('rank_sum', sortedByPer.indexOf(stock));

    stock.setScore('buy_price', stock.getAdjClose());

    });

    var sortedByRankSum = universe.sort(function(b,a) {

        return a.getScore('rank_sum') - b.getScore('rank_sum');

    })

    var modelPortfolio2 =  sortedByRankSum.slice(0, stock_num);

    return modelPortfolio2;

}

// 5) 리밸런싱 수행 - 시뮬레이션 기간 동안 장 마감 후 매일 자동으로 호출되는 함수

function onDayClose(now) {

    KOSDAQ_filter();

    if (IQDate.isRebalancingDay(now) || isFirst === true) {

        logger.debug('리밸런싱을 진행합니다!');

        currentTotalEquity = IQAccount.getDefaultAccount().getTotalEquity();

        logger.debug('현재 계좌 평가액 : ' + Math.floor(currentTotalEquity));

        if (!draw_down_flag) {

            stock_basket.reset();

            stock_basket.setBudget(currentTotalEquity * stock_weight);

        stock_basket.buildPortfolio();

        }

        isFirst = false;                 

    }

}

// 6) 시뮬레이션 종료 함수 - 시뮬레이션이 종료될 때 한번 자동으로 호출

function onComplete() {

    logger.debug("계좌 총 평가금액은 " + Math.floor(IQAccount.getDefaultAccount().getTotalEquity()) + "원 입니다.");

    IQLive.addPortfolio(stock_basket, stock_weight);

}



댓글 0
댓글 등록을 위해서 로그인해주세요.
 
최신 게시글