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

코드를 제대로 한건지 검토 부탁드립니다.

QSSONG 2018.08.06 13:54 조회수  1214 추천 0

 - 전략 : 전종목대상 PBR( 0.3 이상 0.8 이하) + PER (3이상 8이하) 선택 후 

               시가총액 낮은 순으로 20종목 매수.




var account1, account2, account3;

var basket1, basket2, basket3;                // 주식 종목들을 담을 Basket



var STOCK_WEIGHT = 0.95;    // 주식 비율 현금 보유를 0%

var MAX_SIZE1 = 20;         // 바스켓1 사이즈




// 이 전략이 초기화되면 Initialize 함수가 호출됩니다.

function initialize() {

    IQEnvironment.simulationMethod = SimulationMethod.day;   

    IQEnvironment.stockCommission = 0.0015;   // commission for stocks 0.0015

  IQEnvironment.etfCommission = 0.0015;       // commission for ETFs 0.0015

    IQEnvironment.stockTax = 0.013;    // 주식 세금 1.3% (슬리피지 1%포함)

    

    

    var account_1 = IQAccount.addAccount('0000-0000-01', 'SSONG', IQEnvironment.aum);

     


basket1 = new Basket(account_1, MAX_SIZE1, IQEnvironment.aum * STOCK_WEIGHT);    

    

 

    basket1.setPortfolioBuilder(portfolioBuilder1);

 

}



// PCR역순 구하기

function cp(stock) { 

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return  (stock.getFundamentalOperatingCashFlow(0) +

           stock.getFundamentalOperatingCashFlow(1) +

           stock.getFundamentalOperatingCashFlow(2) +

           stock.getFundamentalOperatingCashFlow(3)) / stock.getMarketCapital(); }

// PBR역순 구하기

function PBR(stock) { 

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return  (stock.getFundamentalTotalEquity(0) +

           stock.getFundamentalTotalEquity(1) +

           stock.getFundamentalTotalEquity(2) +

           stock.getFundamentalTotalEquity(3)) / stock.getMarketCapital(); }

// PER역순 구하기

function PER(stock) { 

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return  (stock.getFundamentalNetProfit(0) +

           stock.getFundamentalNetProfit(1) +

           stock.getFundamentalNetProfit(2) +

           stock.getFundamentalNetProfit(3)) / stock.getMarketCapital(); }

// POR역순 구하기

function op(stock) { 

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return  (stock.getFundamentalOperatingIncome(0) +

           stock.getFundamentalOperatingIncome(1) +

           stock.getFundamentalOperatingIncome(2) +

           stock.getFundamentalOperatingIncome(3)) / stock.getMarketCapital(); }

// PSR역순 구하기

function sp(stock) { 

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return  (stock.getFundamentalRevenue(0) +

           stock.getFundamentalRevenue(1) +

           stock.getFundamentalRevenue(2) +

           stock.getFundamentalRevenue(3)) / stock.getMarketCapital(); }

// ev/ebitda역순 구하기

function eev(stock) {

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return  (stock. getFundamentalEBITDA(0) +

           stock. getFundamentalEBITDA(1) +

           stock. getFundamentalEBITDA(2) +

           stock. getFundamentalEBITDA(3)) / stock.getFundamentalEV(); }

// GPA 구하기

function gpa(stock) { return (stock.getFundamentalRevenue() - stock.getFundamentalSalesCost()) / stock.getFundamentalTotalAsset(); }

// GPA 구하기 

function gpa4(stock) {

stock.loadPrevData(2, 0, 0);

  return (stock.getFundamentalRevenue(0) + 

          stock.getFundamentalRevenue(1) + 

          stock.getFundamentalRevenue(2) + 

          stock.getFundamentalRevenue(3) - 

          stock.getFundamentalSalesCost(0) - 

          stock.getFundamentalSalesCost(1) - 

          stock.getFundamentalSalesCost(2) - 

          stock.getFundamentalSalesCost(3) ) / stock.getFundamentalTotalAsset(); }


// GPS 구하기

function gps(stock) { return (stock.getFundamentalRevenue() - stock.getFundamentalSalesCost()) / stock.getMarketCapital(); }

// GPS 구하기 

function gps4(stock) {

stock.loadPrevData(2, 0, 0);

  return (stock.getFundamentalRevenue(0) + 

          stock.getFundamentalRevenue(1) + 

          stock.getFundamentalRevenue(2) + 

          stock.getFundamentalRevenue(3) - 

          stock.getFundamentalSalesCost(0) - 

          stock.getFundamentalSalesCost(1) - 

          stock.getFundamentalSalesCost(2) - 

          stock.getFundamentalSalesCost(3) ) / stock.getMarketCapital(); }


//배당수익률 

function dvs(stock) {

    stock.loadPrevData(2, 0, 0); //이전 재무데이터 로드

  return (stock.getFundamentalDividend(0) +

          stock.getFundamentalDividend(1) +

          stock.getFundamentalDividend(2) +

          stock.getFundamentalDividend(3)) / stock.getMarketCapital(); }


//분기 자산성장률 역수

function AssetGrowth4(stock) {

  stock.loadPrevData(2,0,0); //이전 재무데이터 로드

    var AssetGrowth = 0;

    AssetGrowth =stock.getFundamentalTotalAsset(4)/(stock.getFundamentalTotalAsset(0) - stock.getFundamentalTotalAsset(4));

    return AssetGrowth;

}


//시가총액 역

function P(stock) {return 1/stock.getMarketCapital();}


///////////////////////////


//portfolioBuilder 함수에서 사용할 필터링 함수를 정의합니다.

function stockFilter(stock) {

    if (stock.getMarketCapital() === 0) { return false; }

    if (stock.getClose() === 0) { return false; }                      // 가격 0인 종목 = 상폐종목 제외

    if (stock.getTradingVolume() === 0) { return false; }               // 거래량 0인 종목 = 거래정지 중인 종목 제외    

    if (stock.isETF) { return false; }            // ETF 제외

    if (stock.manage & 1 ) { return false; } // 관리종목 제외

    if (stock.name.substr(stock.name.length -3) === "ETN") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -4) === "투자신탁") { return true; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "스팩") { return false; } //스팩 제외

    if (stock.name.substr(stock.name.length -3) === "스팩2") { return false; } //스팩 제외    

    if (stock.name.substr(stock.name.length -2) === "우B") { return false; } //우선주 제외    

    if (stock.name.substr(stock.name.length -2) === "우C") { return false; } //우선주 제외  

    if (stock.name.substr(stock.name.length -1) === "우") {

        if (stock.name === "연우") { return true;} else { return false; }     }//우선주 제외     

    if (stock.name.substr(stock.name.length -3) === "우선주") { return false; } //우선주 제외          

    if (stock.name.substr(stock.name.length -1) === ")") { return false; } //우선주 제외  

    if (stock.name.substr(stock.name.length -2) === "1호") { return false; } //스팩 제외    

    if (stock.name.substr(stock.name.length -2) === "2호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "3호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "4호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "5호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "6호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "7호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "8호") { return false; } //스팩 제외  

    if (stock.name.substr(stock.name.length -2) === "9호") { return false; } //스팩 제외   

    if (stock.name.substr(stock.name.length -3) === "10호") { return false; } //스팩 제외 

    if (stock.name.substr(stock.name.length -3) === "개발1") { return false; } //스팩 제외     

    if (stock.name.substr(stock.name.length -4) === "SPAC") { return false; } //스팩 제외   

    if (stock.name.substr(0, 2) === "QV") { return false; } //ETN 제외  

    if (stock.name.substr(0, 4) === "TRUE") { return false; } //ETN 제외      

    if (stock.name.substr(0, 3) === "삼성 ") { return false; } //ETN 제외  

    if (stock.name.substr(0, 5) === "미래에셋 ") { return false; } //ETN 제외  

    if (stock.name.substr(0, 3) === "신한 ") { return false; } //ETN 제외  

    if (stock.name.substr(0, 3) === "대신 ") { return false; } //ETN 제외 

                      

    if (stock.getPBR() > 0.8 || stock.getPBR() < 0.3 || stock.getPER() > 8 || stock.getPER() < 3  ) { return false; }

   

    

    return true;

}

//////////////



function portfolioBuilder1(targetSize) {

    

    var universe = IQStock.filter(stockFilter); 

     

    

   // var sortedByPbr = universe.slice().sort( function(a, b) { return bp(b) - bp(a); });

   //  var sortedByper = universe.slice().sort( function(a, b) { return ep(b) - ep(a); });

   //  var sortedByPsr = universe.slice().sort( function(a, b) { return sp(b) - sp(a); });

   // var sortedByPcr = universe.slice().sort( function(a, b) { return cp(b) - cp(a); });

   // var sortedBygpa4 = universe.slice().sort( function(a, b) { return gpa4(b) - gpa4(a); });

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

    

    universe.forEach( function(stock) {// EBSCA4

        stock.setScore('rank_sum', //sortedByPER.indexOf(stock) + 

                     //  sortedByPbr.indexOf(stock) + 

                     //  sortedByper.indexOf(stock) +

                     //  sortedByPsr.indexOf(stock) +

                     //  sortedByPcr.indexOf(stock)

                      // sortedBygpa4.indexOf(stock)

                         sortedbyP.indexOf(stock)

                      ); 

    });

     

    //시총 하위20%

    var sortedByCap = universe.sort( function(a, b) { return a.getMarketCapital() - b.getMarketCapital(); });

    universe.forEach( function(stock) { stock.setScore('cap_rank', sortedByCap.indexOf(stock)); });

    

    var smallCap = universe.filter(function(stock) {

        if ( stock.getScore('cap_rank') > (0.999 * universe.length) ) { return false; }

        return true; } );

    

    var modelPortfolio = smallCap.slice().sort( function(a, b) {

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

    

    var modelPortfolio2 =  modelPortfolio.slice(0, 20);    

    

    logger.debug(modelPortfolio2);

    return modelPortfolio2;

}




///////////////////////////

var lastRebalMonth = -1; 

var startDate = 3;


function onDayClose(now) {

    // 매달 초에 리밸런싱을 수행합니다.

if ((now.getMonth() != lastRebalMonth &&  now.getDate() >= startDate)) {

        

        var account_1 = IQAccount.getAccount('0000-0000-01');

        var Equity1 = account_1.getTotalEquity();

        basket1.setBudget(Equity1 * STOCK_WEIGHT);

        

        basket1.buildPortfolio();   

        basket1.reset();

basket1.targetSize = MAX_SIZE1;

        basket1.buildPortfolio();

        

        lastRebalMonth = now.getMonth();

}


}

댓글 5
네 맞습니다.
설명에 //시총 하위 20%는 시총 하위 20종목입니다.
그리고 onDayClose 함수에 basket1.targetSize = MAX_SIZE1;는 할 필요가 없습니다.
이미 20종목만 modelPortfolio2에 선택해두어서 중복입니다. 
바스켓의 종목수를 정해두어도 포트폴리오에 그 이상 담으면 넘치는데, 이 경우에 쓰시면 됩니다.

스코어를 사용할 수 있는 확장 가능한 형식이라 길지만, 
시총 하위 20종목만 고르신다면 스코어를 매기지 않아도
var modelPortfolio2 =  sortedByCap.slice(0, 20); 
로 가능합니다.
퀀트퀸 2018.08.06 19:56
자세한 설명 감사합니다.
말씀해 주신대로  수정해 보겠습니다..
QSSONG 2018.08.06 21:28
바스켓이 20종목 설정인데, 왜 항상 종목수가 미달일까요?
QSSONG 2018.08.13 09:00
위의 설명에 썻지만, 바스켓을 20종목으로 정해두어도, 
포트폴리오에서 20종목 넘게 집어 넣으면 들어갑니다.
즉, 포트폴리오에서 20종목 이하로 선택되었습니다.
보통은 필터의 조건이 강할수록 적게 선택됩니다.

필터의 가장 강한 조건은 아래입니다.
 if (stock.getPBR() > 0.8 || stock.getPBR() < 0.3 || stock.getPER() > 8 || stock.getPER() < 3  ) 
그래서 확인할 사항은, 이 필터를 거친 후 포트폴리오의 종목 수입니다.
꽤 강한 조건이라 저평가주의 수가 적은 것으로 추측됩니다.
퀀트퀸 2018.08.13 17:24
아~네   그렇군요.
 감사합니다.
QSSONG 2018.08.13 23:28
댓글 등록을 위해서 로그인해주세요.
 
최신 게시글