Search

예제검토2. 종목검색 매수 후 차트에 적용한 예스랭귀지 볼린저밴드 상단으로 매도

종목검색으로 매수한 종목을 3분봉 차트의 볼린저밴드 상단선으로 감시해 매도하는 예제입니다.
볼린저밴드 같은 지표값은 스팟 수식 안에서 직접 계산하지 않습니다. 예스랭귀지로 지표식을 만들어 저장해 두고, 스팟에서 차트를 만들 때 그 지표를 함께 적용한 뒤 GetIndicatorData 로 값을 읽어 매매 판단에 씁니다. 지표 계산은 예스랭귀지가, 매매 판단은 스팟이 맡는 구조입니다.
아래 내용은 손절은 즉시, 이익보존과 이익실현은 볼린저밴드 상단선을 이탈했을 때 매도하는 조건을 구현한 것이며 다른 지표와 다른 청산 조건에도 같은 구조를 응용할 수 있습니다.
1.
전략내용
5초 주기로 종목검색을 실행하고 검색된 종목을 종목당 매수금만큼 시장가로 매수합니다. 매수한 종목마다 3분봉 차트확장객체를 만들면서 예스랭귀지 지표 "볼밴상단" 을 함께 적용합니다.
2초마다 보유 종목의 수익률을 확인해 손절 기준(-2%) 이하이면 즉시 시장가로 매도합니다. 이익보존 기준(3.5%) 이상이면 현재가가 볼린저밴드 상단값 아래로 내려왔을 때만 매도하며, 이때 수익률이 이익실현 기준(7%) 이상이면 이익실현, 미만이면 이익보존으로 구분합니다. 현재가가 상단값 이상이면 상승 추세가 이어진다고 보고 보유합니다.
스팟을 시작할 때 계좌에 이미 보유한 종목이 있으면 그 종목들도 차트를 만들어 감시 대상에 넣습니다.
2.
스크립트 객체화면 설정
계좌객체를 추가한 후 속성에서 객체명은 Account1 으로 지정하고 계좌번호를 지정합니다.
종목객체와 차트확장객체는 추가하지 않습니다. 수식 안에서 Main.ReqMarketData 와 Main.ReqChartEx 로 만들어 씁니다.
3.
지표식 준비 (예스랭귀지)
스팟 수식을 돌리기 전에 아래 지표식을 예스랭귀지 지표식 편집기에서 작성하고 "볼밴상단" 이라는 이름으로 저장합니다.
var : BBup(0); BBup = BollBandUp(20,2); Plot1(BBup, "상단밴드");
Plain Text
복사
지표식으로 작성하고 문법검증(F4) 후에 F5 키를 누르면 지표속성창이 나타납니다. 지표속성창 Y축표시탭에서 가격으로 설정하고 식작성을 완료합니다. 저장한 이름은 스팟수식의 지표명 변수에 적는 이름과 글자 하나까지 같아야 합니다.
4.
사용자검색조건 설정
스팟에서는 Main.ReqPowerSearch("사용자검색조건명"); 함수로 종목검색을 수행합니다. 파워종목검색 화면에서 종목검색식을 조건리스트에 추가하고 주기 등 설정을 완료한 후에 조건저장 버튼으로 저장한 사용자검색조건명을 스팟수식의 SearchName 변수에 지정합니다. 예제의 "기본전략" 은 예시 이름입니다.
5.
스팟수식
//종목검색으로 매수한 종목을 3분봉 차트에 붙인 예스랭귀지 지표(볼린저밴드 상단)로 감시해 //손절, 이익보존, 이익실현 조건에 따라 매도하는 예제 //게시판 문의: 이익보존 3.5% 와 이익실현 7% 에 도달해도 3분봉 볼린저밴드 상단선을 이탈하기 전에는 보류하고, //이탈하면 익절한다. 볼린저밴드는 예스랭귀지 지표식 "볼밴상단" 을 차트에 적용해 읽는다 var SearchName = "기본전략"; //파워종목검색 화면에서 조건저장으로 만든 사용자검색조건명 var timer5 = 5; // 5초 간격 종목검색 var 매수금 = 50000; var Exchange = 1; //시세주문 거래소 - 1:시세(통합)/주문(SOR,스마트), 2: 시세(KRX)/주문(KRX) , 4 시세(NXT)/주문(NXT) var OrderList = []; var MKList = []; var ChartList = []; // 3분봉 차트 객체 저장 배열 var B = []; var req,S; var 지표명 = "볼밴상단";//예스랭귀지 지표명 // 매도 조건 설정 var SELL_GAIN = 7.0; // 이익실현 기준 7% var SELL_BREAK = 3.5; // 이익보존 기준 3.5% var SELL_LOSS = -2.0; // 손절 -2% function Main_OnStart() { Main.MessageList("자동매매 시작 (익절/보존: 볼린저밴드 3분 20,2 연동)"); S = 0; // 기존 계좌에 있는 종목들에 대해서도 3분봉 차트를 요청 var num = Account1.GetTheNumberOfBalances(); if (num > 0) { Main.MessageList("보유종목있음:",num); OrderList = []; for (var i = 0; i < num; i++) { Account1.SetBalance(i); OrderList.push(Account1.Balance.code); } req = 0; Main.ReqMarketData({code:OrderList[req], exchangeKind:Exchange}); Main.MessageList("종목객체요청:",OrderList[req],"|거래소:",Exchange); } else { Main.MessageList("보유종목없음:타이머 셋팅"); // 1번 타이머: 종목 검색용 (5초) Main.SetTimer(1, timer5 * 1000); } // 3번 타이머: 실시간 잔고 수익률 감시용 (2초마다 체크) Main.SetTimer(3, 2000); } function Main_OnTimer(nEventID) { var d = new Date(); var HHMMSS = d.getHours()*10000 + d.getMinutes()*100 + d.getSeconds(); // 1. 매수 종목 검색 (08:00 ~ 20:00) if (nEventID == 1 && HHMMSS > 80000 && HHMMSS < 200000) { Main.ReqPowerSearch(SearchName); Main.MessageList("종목검색실행"); S = 1; } // 2. 종목 객체 요청 지연 처리 if (nEventID == 2) { Main.KillTimer(2); Main.ReqMarketData({code:OrderList[req], exchangeKind:Exchange}); Main.MessageList("종목객체 재요청:",OrderList[req],"|거래소:",Exchange); } // 3. 매도 감시 로직 (수익률 + 볼린저밴드 체크) if (nEventID == 3 && ChartList.length > 0) { for (var i = 0; i < ChartList.length; i++) { // 차트객체의 종목코드로 잔고를 찾는다 var sCode = ChartList[i].GetCode(1); Account1.SetBalance(sCode, 0); if (Account1.Balance.count == 0) { Main.MessageList(sCode,"|잔고체크|보유수량없음"); } else { var OP = MKList[i].current; for (var j = 5; j >= 1; j--) { if (MKList[i].Bid(j) > 0) { OP = MKList[i].Bid(j); break; } } var curPrice = Account1.Balance.current; var PLrate = (curPrice - Account1.Balance.avgUnitCost)/Account1.Balance.avgUnitCost * 100; // 차트에 적용한 예스랭귀지 지표에서 볼린저밴드 상단값을 읽는다. 봉 인덱스 0 은 현재 진행 중인 봉 var bbUp = ChartList[i].GetIndicatorData(지표명,1,0); Main.MessageList(sCode,"|잔고체크|수익률:",PLrate.toFixed(2),"|현재가:",curPrice,"|볼밴상단:",bbUp.toFixed(2)); // 손절 처리 (볼밴 무관하게 -2% 도달 시 즉시 매도) if (PLrate <= SELL_LOSS && B[i] == 1) { Account1.OrderSell({exchangeKind:Exchange, code:sCode, count:Account1.Balance.count, orderPrice:OP, priceKind:0}); B[i] = -1; Main.MessageList("▼손절(-2%) 매도:", sCode, "수익률:", PLrate.toFixed(2)); continue; // 손절했으므로 다음 종목으로 넘어감 } // 수익권(3.5% 이상)일 때 볼린저밴드 상한선 이탈 확인 if (PLrate >= SELL_BREAK && B[i] == 1) { // 이탈 조건: 현재가가 상한선 아래에 있을 때 매도 if (curPrice < bbUp) { if (PLrate >= SELL_GAIN) { Account1.OrderSell({exchangeKind:Exchange, code:sCode, count:Account1.Balance.count, orderPrice:OP, priceKind:0}); Main.MessageList("★이익실현(7%이상) 매도 - 상한선 이탈:", sCode, "수익률:", PLrate.toFixed(2)); B[i] = -1; } else if (PLrate >= SELL_BREAK && PLrate < SELL_GAIN) { Account1.OrderSell({exchangeKind:Exchange, code:sCode, count:Account1.Balance.count, orderPrice:OP, priceKind:0}); Main.MessageList("●이익보존(3.5%이상) 매도 - 상한선 이탈:", sCode, "수익률:", PLrate.toFixed(2)); B[i] = -1; } } // 주가가 상한선보다 크거나 같으면 (curPrice >= bbUp) 홀딩! } } } } } function Main_OnRcvItemList(aItemList, nCount) { Main.MessageList("종목검색완료"); Main.KillTimer(1); OrderList = []; if (nCount >= 1) { for (var a = 0; a < nCount; a++) { var Add = true; for (var b = 0; b < MKList.length; b++) { if (aItemList[a] == MKList[b].code) { Add = false; } } if (Add == true && !IsStockInAccount(aItemList[a])) { OrderList.push(aItemList[a]); } } } if (OrderList.length == 0) { Main.SetTimer(1, timer5 * 1000); Main.MessageList("주문종목이없음:타이머재실행"); } else { req = 0; Main.ReqMarketData({code:OrderList[req], exchangeKind:Exchange}); Main.MessageList("종목객체요청:",OrderList[req],"|거래소:",Exchange); } } function Main_OnRcvMarketData(MarketData) { if (MarketData.code == OrderList[req]) { Main.MessageList("종목객체생성:",MarketData.code,"|거래소:",MarketData.exchangeKind); MKList.push(MarketData); B.push(1); if (S == 1 && !IsStockInAccount(MarketData.code)) { // 매수 주문 실행 //수량산정시 매도1호가를 사용(매도1호가가 없으면 현재가로 대체) var OP = MarketData.current; if (MarketData.Ask(1) > 0) { OP = MarketData.Ask(1); } var vol = Math.floor(매수금 / OP); if (OP > 0 && vol > 0) { Account1.OrderBuy({exchangeKind:Exchange, code:MarketData.code, count:vol, orderPrice:OP, priceKind:0}); Main.MessageList("매수수행:",MarketData.code); } else { Main.MessageList("매수금부족"); } } //차트 설정(3분봉 5000개, 갭보정 안함, 수정주가 처리 안함). 예스랭귀지 지표 "볼밴상단" 을 함께 적용한다 var ChartSet = new ReqChartItem({code:OrderList[req], exchangeKind:Exchange},3,CHART_PERIOD_MINUTE, 5000, CHART_REQCOUNT_BAR, false, false); var IndSet = new Array(new IndicatorInfo(지표명)); Main.ReqChartEx(ChartSet,null,IndSet); Main.MessageList("차트객체요청:",OrderList[req]); } } function Main_OnRcvChartEx(ChartEx) { if (ChartEx.GetCode(1) == OrderList[req]) { Main.MessageList("차트객체생성:",ChartEx.GetCode(1)); ChartList.push(ChartEx); req = req + 1; if (req < OrderList.length) { var aa = Main.ReqMarketData({code:OrderList[req], exchangeKind:Exchange}); Main.MessageList("종목객체요청:",OrderList[req],"|거래소:",Exchange); if (aa == -1) { Main.SetTimer(2, 15000); } } else { Main.SetTimer(1, timer5 * 1000); } } } function IsStockInAccount(stockCode) { var numberOfBalances = Account1.GetTheNumberOfBalances(); for (var i = 0; i < numberOfBalances; i++) { Account1.SetBalanceIndex(i); if (stockCode == Account1.Balance.code) { return true; } } return false; }
JavaScript
복사