커뮤니티

예스스팟 관련해서

프로필 이미지
victor
2026-05-14 00:15:32
103
글번호 232034

안녕하세요.

일전에 도움을 받아서 매도 조건를 수정 했었는 데 손실이 너무 커서아래와 같이 매도를 수정 하려고 합니다.

1. 이익보존 3.5 % 에 익절 하기전에 3분봉 기준 볼린저밴드(a,a)상한선 이탈 하지 않았으면 익절 하지 않고 보류 하고 있다가 볼린저밴드( a,a )상한선 이탈 하면 익절 합니다.

이익보존 3. 5%에 도달 했을 때 3분봉 기준 볼린저밴드(a,a)상한선 이탈 했으면 익절 합니다.

2 이익실현 7%이 익절 하기전 3분봉 기준 볼린저밴드(a,a) 상한선 이탈 하지 않았으면 익절 하지 않고 있다가 3분봉 기준 볼린저밴드( a,a) 상한선 이탈하면 익절한다.

이익실현 7%에 도달 했을 때 3분봉 기준 볼린저밴드(a,a) 상한선 이탈 했으면 익절한다.

AI 도움으로 아래와 같이 만들었는 데, 수고스럽지만 확인 좀 부탁 드립니다.

늘 수고 해주심에 머리 숙여 감사함을 전 합니다.


var timer5 = 5; // 5초 간격 종목검색

var 매수금 = 50000;

var OrderList = [];

var MKList = [];

var ChartList = []; // 3분봉 차트 객체 저장 배열

var req;


// 매도 조건 설정

var SELL_GAIN = 7.0; // 이익실현 기준 7%

var SELL_BREAK = 3.5; // 이익보존 기준 3.5%

var SELL_LOSS = -2.0; // 손절 -2%


// 볼린저밴드 변수

var BB_PERIOD = a; // 볼린저밴드 기간

var BB_D = a; // 볼린저밴드 승수


function Main_OnStart()

{

// 1번 타이머: 종목 검색용 (5초)

Main.SetTimer(1, timer5 * 1000);

// 3번 타이머: 실시간 잔고 수익률 감시용 (2초마다 체크)

Main.SetTimer(3, 2000);

MKList = [];

ChartList = [];

Main.MessageList("자동매매 시작 (익절/보존: 볼린저밴드 3분 a,a 연동)");


// 기존 계좌에 있는 종목들에 대해서도 3분봉 차트를 요청

var num = Account1.GetTheNumberOfBalances();

for (var i = 0; i < num; i++) {

Account1.SetBalanceIndex(i);

RequestChartEx(Account1.Balance.code);

}

}


function Main_OnTimer(nEventID)

{

var d = new Date();

var HHMMSS = d.getHours()*10000 + d.getMinutes()*100 + d.getSeconds();


// 1. 매수 종목 검색 (09:00 ~ 15:20)

if (nEventID == 1 && HHMMSS > 090000 && HHMMSS < 152000)

{

Main.ReqPowerSearch("기본전략");

}


// 2. 종목 객체 요청 지연 처리

if (nEventID == 2)

{

Main.KillTimer(2);

Main.ReqMarketData(OrderList[req]);

}


// 3. 매도 감시 로직 (수익률 + 볼린저밴드 체크)

if (nEventID == 3)

{

var num = Account1.GetTheNumberOfBalances();

for (var i = 0; i < num; i++)

{

Account1.SetBalanceIndex(i);

var code = Account1.Balance.code;

var currentRate = (Account1.Balance.current / Account1.Balance.avgUnitCost - 1) * 100;

var currentPrice = Account1.Balance.current;


// 손절 처리 (볼밴 무관하게 -2% 도달 시 즉시 매도)

if (currentRate <= SELL_LOSS)

{

Account1.OrderSell(code, Account1.Balance.count, 0, 1);

Main.MessageList("▼손절(-2%) 매도:", code, "수익률:", currentRate.toFixed(2));

continue; // 손절했으므로 다음 종목으로 넘어감

}


// 수익권(3.5% 이상)일 때 볼린저밴드 상한선 계산 및 이탈 확인

if (currentRate >= SELL_BREAK)

{

var myChart = GetChartObj(code); // 해당 종목의 3분봉 차트 객체 찾기

if (myChart != null && myChart.GetReqCount() >= BB_PERIOD)

{

// 볼린저밴드 상한선 계산 로직 (MA + D * StdDev)

var sum = 0;

for (var k = 0; k < BB_PERIOD; k++) {

sum += myChart.GetClose(1, k);

}

var ma = sum / BB_PERIOD;

var sumVar = 0;

for (var k = 0; k < BB_PERIOD; k++) {

var diff = myChart.GetClose(1, k) - ma;

sumVar += (diff * diff);

}

var stddev = Math.sqrt(sumVar / BB_PERIOD);

var bbUp = ma + (BB_D * stddev); // 볼린저밴드 상한선


// 이탈 조건: 현재가가 상한선 아래에 있을 때 매도

if (currentPrice < bbUp)

{

if (currentRate >= SELL_GAIN) {

Account1.OrderSell(code, Account1.Balance.count, 0, 1);

Main.MessageList("★이익실현(7%이상) 매도 - 상한선 이탈:", code, "수익률:", currentRate.toFixed(2));

}

else if (currentRate >= SELL_BREAK && currentRate < SELL_GAIN) {

Account1.OrderSell(code, Account1.Balance.count, 0, 1);

Main.MessageList("●이익보존(3.5%이상) 매도 - 상한선 이탈:", code, "수익률:", currentRate.toFixed(2));

}

}

// 주가가 상한선보다 크거나 같으면 (currentPrice >= bbUp) 홀딩!

}

}

}

}

}


function Main_OnRcvItemList(aItemList, nCount)

{

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);

} else {

req = 0;

Main.ReqMarketData(OrderList[req]);

}

}


function Main_OnRcvMarketData(MarketData)

{

if (MarketData.code == OrderList[req])

{

MKList.push(MarketData);

if (!IsStockInAccount(MarketData.code))

{

// 매수 주문 실행

Account1.OrderBuy(MarketData.code, Math.floor(매수금 / MarketData.Ask(1)), 0, 1);

Main.MessageList(MarketData.code + " 매수 수행");

// 매수와 동시에 3분봉 차트 객체 요청 (볼밴 감시용)

RequestChartEx(MarketData.code);

}


req = req + 1;

if (req < OrderList.length)

{

var aa = Main.ReqMarketData(OrderList[req]);

if (aa == -1) Main.SetTimer(2, 15000);

}

else

{

Main.SetTimer(1, timer5 * 1000);

}

}

}


// ---------------------- 차트 객체 관련 함수 ---------------------- //


// 3분봉 차트 요청 함수

function RequestChartEx(code) {

// 중복 요청 방지

for (var i = 0; i < ChartList.length; i++) {

if (ChartList[i].GetCode(1) == code) return;

}

// ReqChartItem(종목코드, 주기, 주기구분, 조회건수, 조회건수구분, 수정주가여부, 갭보정여부)

var reqChart = new ReqChartItem(code, 3, CHART_PERIOD_MINUTE, 50, CHART_REQCOUNT_BAR, false, false);

Main.ReqChartEx(reqChart);

}


// 요청한 차트 객체 수신 시 배열에 저장

function Main_OnRcvChartEx(ChartEx)

{

ChartList.push(ChartEx);

}


// 배열에서 특정 종목의 차트 객체 찾기

function GetChartObj(code) {

for (var i = 0; i < ChartList.length; i++) {

if (ChartList[i].GetCode(1) == code) {

return ChartList[i];

}

}

return null;

}


// ----------------------------------------------------------------- //


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;

}



답변 2
프로필 이미지

예스스탁 예스스탁 답변

2026-05-21 14:36:06

안녕하세요 예스스탁입니다. 1 수식을 수정해 드립니다. 올려주신 수식이 객체에서 제공하지 않는 함수들을 사용하는 경우도 있고 잘못 작성된 부분으로 인해 적용시 프로그램이 강제종료되는 경우가 있었습니다. 2 수식에서 차트를 만들때 예스랭귀지로 만든 볼린져밴드 지표 적용해서 생성할 수 있습니다. 아래 수식을 볼밴상단이라는 이름으로 만드신 후에 스팟식에서 차트객체 만들때 같이 적용되어 생성되게 하시면 됩니다. var : BBup(0); BBup = BollBandUp(20,2); Plot1(BBup, "상단밴드"); 지표식으로 작성하시고 문법검증(f4) 후에 f5키를 누르시면 지표속성창이 나타납니다. 지표속성창 Y축표시탭에서 가격으로 설정하고 식작성 완료하시면 됩니다. 3 var timer5 = 5; // 5초 간격 종목검색 var 매수금 = 50000; 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% // 볼린저밴드 변수 var BB_PERIOD = 20; // 볼린저밴드 기간 var BB_D = 2; // 볼린저밴드 승수 function Main_OnStart() { Main.MessageList("자동매매 시작 (익절/보존: 볼린저밴드 3분 a,a 연동)"); 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(OrderList[req]); Main.MessageList("종목객체요청:",OrderList[req]); } 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. 매수 종목 검색 (09:00 ~ 15:20) if (nEventID == 1 && HHMMSS > 090000 && HHMMSS < 152000) { Main.ReqPowerSearch("기본전략"); Main.MessageList("종목검색실행"); S = 1; } // 2. 종목 객체 요청 지연 처리 if (nEventID == 2) { Main.KillTimer(2); Main.ReqMarketData(OrderList[req]); } // 3. 매도 감시 로직 (수익률 + 볼린저밴드 체크) if (nEventID == 3 && ChartList.length > 0) { for (var i = 0; i < ChartList.length; i++) { Account1.SetBalance(i); if (Account1.Balance.count == 0) { Main.MessageList(ChartList[i].GetCode(1),"|잔고체크|보유수량없음"); } else { var curPrice = Account1.Balance.current; var PLrate = (curPrice - Account1.Balance.avgUnitCost)/Account1.Balance.avgUnitCost * 100; Main.MessageList(Account1.Balance.code,"|잔고체크|수익률:",PLrate.toFixed(2), "|현재가:",curPrice,"|볼밴상단:",ChartList[i].GetIndicatorData(지표명,1,0).toFixed(2)); // 손절 처리 (볼밴 무관하게 -2% 도달 시 즉시 매도) if (PLrate <= SELL_LOSS && B[i] == 1) { Account1.OrderSell(Account1.Balance.code, Account1.Balance.count, 0, 1); B[i] = -1; Main.MessageList("▼손절(-2%) 매도:", Account1.Balance.code, "수익률:", currentRate.toFixed(2)); continue; // 손절했으므로 다음 종목으로 넘어감 } // 수익권(3.5% 이상)일 때 볼린저밴드 상한선 계산 및 이탈 확인 if (PLrate >= SELL_BREAK) { // 이탈 조건: 현재가가 상한선 아래에 있을 때 매도 if (curPrice < bbUp) { if (PLrate >= SELL_GAIN) { Account1.OrderSell(Account1.Balance.code, Account1.Balance.count, 0, 1); Main.MessageList("★이익실현(7%이상) 매도 - 상한선 이탈:", Account1.Balance.code, "수익률:", currentRate.toFixed(2)); B[i] = -1; } else if (PLrate >= SELL_BREAK && PLrate < SELL_GAIN) { Account1.OrderSell(Account1.Balance.code, Account1.Balance.count, 0, 1); Main.MessageList("●이익보존(3.5%이상) 매도 - 상한선 이탈:", Account1.Balance.code, "수익률:", currentRate.toFixed(2)); B[i] = -1; } } // 주가가 상한선보다 크거나 같으면 (currentPrice >= 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(OrderList[req]); Main.MessageList("종목객체요청:",OrderList[req]); } } function Main_OnRcvMarketData(MarketData) { if (MarketData.code == OrderList[req]) { Main.MessageList("종목객체생성:",MarketData.code); MKList.push(MarketData); B.push(1); if (S == 1 && !IsStockInAccount(MarketData.code)) { // 매수 주문 실행 var vol = Math.floor(매수금 / MarketData.Ask(1)); if (vol > 0) { Account1.OrderBuy(MarketData.code,vol, 0, 1); Main.MessageList("매수수행:",MarketData.code); } else { Main.MessageList("매수금부족"); } } //기본종목셋팅(연결선물 주간장,5분 5000개, 갭보정안함, 수정주가처리 안함) var ChartSet = new ReqChartItem(OrderList[req],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(OrderList[req]); 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; } 즐거운 하루되세요
프로필 이미지

victor

2026-05-22 09:51:04

victor 님에 의해 삭제된 답변입니다.