커뮤니티

예스스팟 Q&A

답변완료

시장가 매도 오류 관련

안녕하세요 오늘 스팟으로 시장가매도 주문을 넣었는데 주문가격이 0으로 나가 신규주문 오류가 발생했습니다. 어제까지만해도 잘돌아가던 로직인데, 혹시 내부적으로 수정된게 있는지요??
프로필 이미지
깅창
127
글번호 226329
답변완료

시스템식 검증 부탁드립니다.

// ========================================================= // 1. 전역 변수 설정 및 매매 전략 스위치/설정 섹션 // ========================================================= // --- 기본 프로그램 설정 --- var timerInterval = 5; // 5초 타이머 간격 (매 5초마다 특정 작업을 수행하도록 설정) var HHMMSS = 0; // 현재 시분초 저장을 위한 변수 // --- 종목 관리 변수 --- var MKList = []; // 검색 결과 종목 코드를 저장할 배열 var MK = []; // 실제 MarketData 객체(매수된 종목 등)가 담길 배열 var req = 0; // MKList에서 몇 번째 종목을 요청 중인지 나타내는 인덱스 // --- [? 중요 ?] 예수금 및 매수 설정 --- // 초기 자산 설정 (전략 내에서 관리하거나 가상으로 설정할 금액) // 실제 계좌 잔고는 Account1.Balance 등으로 확인됩니다. var INITIAL_ASSET_AMOUNT = 5000000; // 500만원 (예시) // 1회 매수 시 투입할 금액 (동일 종목 매수 시 한 번에 진입할 금액) var ONE_TIME_BUY_AMOUNT = 100000; // 10만원 // 수익/손실 잔고 확인은 Main_OnRcvMarketData 또는 Main_OnTimer 내에서 // Account1 객체를 통해 실시간으로 확인하고 로그 출력 // --- [? 중요 ?] 매매 조건 활성화/비활성화 스위치 --- var ENABLE_TRADING = true; // true: 전체 매매 로직 활성화, false: 모든 매매 중지 // 각 매수 조건 활성화/비활성화 스위치 (true: 활성화, false: 비활성화) var USE_BUY_TYPE1_DNLINE = true; // 매수Type_1: 볼린저밴드 하단선 상향 돌파 var USE_BUY_TYPE2_MA_HIGH_CROSS = true; // 매수Type_2: 고가 5일MA가 고가 20일MA 상향 돌파 var USE_BUY_TYPE3_C_MA05_HIGH = true; // 매수Type_3: 종가 고가 5일MA 상향 돌파 AND 종가 CumulativeAvg 아래 var USE_BUY_TYPE4_C_MA20H_MIDL = true; // 매수Type_4: 종가 고가 20일MA & 미드라인 상향 돌파 var USE_BUY_TYPE5_ONCE = true; // 매수Type_5: 일단 (CrossDownFlag_MA05_MA20 && C < CumulativeAvg && C CrossUp CrossUp_DnLine_추적) var USE_BUY_TYPE6_C_UP_기준고가 = true; // 매수Type_6: 현재가 UpLine 위에 있고, 이전 기준고가 돌파 var USE_BUY_TYPE7_THRICE = true; // 매수Type_7: 볼린저밴드 상단선 상향 돌파 var USE_BUY_TYPE8_TWICE_CUM_AVG = true; // 매수Type_8: 누적평균선 상향 돌파 var USE_BUY_TYPE9_BB_CONTRACTION_BREAKOUT = true; // 매수Type_9: BB 수축 후 상단선 돌파 // 각 매도 조건 활성화/비활성화 스위치 (true: 활성화, false: 비활성화) var USE_SELL_TYPE1_THRICE_BREAKDOWN = true; // 매도Type_1: 삼단 고가 위에서 MA05_High 또는 UpLine 하향 돌파 시 var USE_SELL_TYPE2_KIJUN_HIGH_BREAKDOWN = true; // 매도Type_2: 갱신된 기준고가 라인 하향 돌파 시 // --- [? 중요 ?] 1일 최대 거래 횟수 제한 설정 --- var MAX_TRADES_PER_DAY = 10; // 하루에 총 매수/매도 거래를 포함한 최대 거래 횟수 (매수 1회, 매도 1회가 각각 1회로 카운트) var dailyTradeCount = 0; // 당일 총 거래 횟수 카운터 var lastTradeDate = ""; // 마지막 거래일 (YYYYMMDD 형식, 초기화 및 날짜 변경 감지에 사용) // --- [? 중요 ?] 종목검색기 이름 설정 --- // 예스스팟에 등록된 검색식 이름과 정확히 일치해야 합니다. var POWER_SEARCH_NAME = "##5분봉 기준 고가 돌파"; // 청산 손절은 시스템설정으로 진행 (예스스팟 자동매매 설정창에서 설정 권장) // --- [? 추가된 기능 ?] 검색된 종목 중 매수할 종목 개수 설정 --- // 검색된 종목 리스트(MKList)에서 실제로 MarketData를 요청하고 매수를 시도할 종목의 최대 개수 // 1로 설정하면 검색된 종목 중 가장 첫 번째 종목만 매수를 시도합니다. var MAX_REQUEST_MARKET_DATA_COUNT = 1; // 기본값: 1개 종목만 매수 시도 // ========================================================= // 2. 예스트레이더 전략 지표 및 상태 변수 (내부 사용) // ========================================================= var BBPeriod = 60; // 볼린저밴드 기간 var BBMul = 2; // 볼린저밴드 배수 var MAPeriod = 20; // MA 기간 (20일 이동평균선에 사용) var CumulativePeriod = 240; // 누적 평균 계산에 사용할 봉 개수 (약 1년 기준) // 지표 값 및 상태 저장을 위한 변수 var 기준 = 0; var 기준봉_신호발생 = false; var 기준_고가 = 0; // 계단식 유지 var 기준_중심 = 0; // 계단식 유지 var 기준_저가 = 0; // 계단식 유지 var MidLine = 0; var UpLine = 0; var DnLine = 0; var StdDevVal = 0; var MA20 = 0; var MA20_High = 0; var MA05_High = 0; var Signal1Occurred = false; // 체크 또는 준비 신호 발생 여부 플래그 (계단식 유지) var CrossUpOccurredClose = 0; // 현재 봉에서 종가가 20MA 상향 돌파 시 종가 저장 var CumulativeAvg = 0; // 누적 평균 값을 저장할 변수 (계산 시 필요) var Decline_Low = 0; // 하락 신호 발생 시 저가를 저장할 변수 (계단식 유지) var BBWidth = 0; // 볼린저밴드 폭 (UpLine - DnLine) var BBWidth_Prev = 0; // 이전 봉의 볼린저밴드 폭 var BBExpanded = false; // 볼린저밴드가 팽창했는지 여부 (계단식 유지) var BBContracted = false; // 볼린저밴드가 수축했는지 여부 (계단식 유지) var ThriceSignalHigh = 0; // 삼단 신호 발생 시의 고가 저장 (계단식 유지) var CrossUp_DnLine_추적 = 0; // 준비 신호 발생 시 종가를 저장 (계단식 유지) var CrossUpFlag_MA05_MA20 = false; // MA05_High가 MA20_High를 상향 돌파했는지 여부 (계단식 유지) var CrossDownFlag_MA05_MA20 = false; // MA05_High가 MA20_High를 하향 돌파했는지 여부 (계단식 유지) var CrossUpFlag_C_DnLine = false; // 종가가 DnLine을 상향 돌파했는지 여부 (계단식 유지) // ========================================================= // 3. 자바스크립트 헬퍼 함수 (지표 계산 및 CrossUp/CrossDown 구현) // ========================================================= // 이동평균선 계산 함수 // dataArray: [현재봉, 1봉전, 2봉전...] 순서의 데이터 배열 function calculateMA(dataArray, period) { if (dataArray.length < period) return 0; var sum = 0; for (var i = 0; i < period; i++) { sum += dataArray[i]; } return sum / period; } // 표준편차 계산 함수 // dataArray: [현재봉, 1봉전, 2봉전...] 순서의 데이터 배열 function calculateSTD(dataArray, period) { if (dataArray.length < period) return 0; var sum = 0; for (var i = 0; i < period; i++) { sum += dataArray[i]; } var mean = sum / period; var sumOfSquares = 0; for (var i = 0; i < period; i++) { sumOfSquares += Math.pow(dataArray[i] - mean, 2); } return Math.sqrt(sumOfSquares / period); } // 상향 돌파 체크 함수 // currentVal: 현재 값, prevVal: 이전 값 // currentRefVal: 현재 기준 값, prevRefVal: 이전 기준 값 function isCrossUp(currentVal, prevVal, currentRefVal, prevRefVal) { return prevVal < prevRefVal && currentVal > currentRefVal; } // 하향 돌파 체크 함수 function isCrossDown(currentVal, prevVal, currentRefVal, prevRefVal) { return prevVal > prevRefVal && currentVal < currentRefVal; } // MarketData에서 최근 N개의 데이터를 배열로 추출하는 헬퍼 함수 // MarketData 객체의 과거 데이터 접근 방식에 따라 수정이 필요할 수 있습니다. // 여기서는 MarketData.Close[i] 와 같은 배열 인덱싱 방식을 가정합니다. function getRecentData(marketDataField, period) { var arr = []; for (var i = 0; i < period; i++) { // marketDataField[i]가 존재하는지 확인 (데이터 부족 시 undefined) if (typeof marketDataField[i] !== undefined) { arr.push(marketDataField[i]); } else { break; // 데이터가 부족하면 루프 중단 } } return arr; // [현재봉, 1봉전, 2봉전...] 순서로 반환 } // ========================================================= // 4. 프로그램 시작 시 실행되는 함수: Main_OnStart() // ========================================================= function Main_OnStart() { var d = new Date(); HHMMSS = d.getHours()*10000 + d.getMinutes()*100 + d.getSeconds(); var today = "" + d.getFullYear() + (d.getMonth() + 1 < 10 ? "0" : "") + (d.getMonth() + 1) + (d.getDate() < 10 ? "0" : "") + d.getDate(); lastTradeDate = today; // 프로그램 시작 시 오늘 날짜로 초기화 Main.MessageList(HHMMSS,"|자동매매 시작"); Main.SetTimer(1, timerInterval*1000); // 1번 타이머를 (5초 * 1000) = 5000ms 간격으로 설정 } // ========================================================= // 5. 타이머가 울릴 때마다 실행되는 함수: Main_OnTimer(nEventID) // ========================================================= function Main_OnTimer(nEventID) { var d = new Date(); HHMMSS = d.getHours()*10000 + d.getMinutes()*100 + d.getSeconds(); var today = "" + d.getFullYear() + (d.getMonth() + 1 < 10 ? "0" : "") + (d.getMonth() + 1) + (d.getDate() < 10 ? "0" : "") + d.getDate(); // 날짜가 바뀌면 일일 총 거래 횟수 초기화 if (dailyTradeCount > 0 && today !== lastTradeDate) { dailyTradeCount = 0; lastTradeDate = today; Main.MessageList(HHMMSS, "|일일 총 거래 횟수 초기화"); } // 1번 타이머 (종목 검색 요청) if (nEventID == 1 && HHMMSS >= 90000) // 오전 9시 이후에만 검색 { Main.MessageList(HHMMSS,"|종목검색요청: " + POWER_SEARCH_NAME); Main.ReqPowerSearch(POWER_SEARCH_NAME); // 설정된 검색기 이름 사용 } // 2번 타이머 (종목 객체 재요청 - API 제한 발생 시) if (nEventID == 2) { Main.KillTimer(2); // 2번 타이머 중지 Main.MessageList(HHMMSS,"|종목객체 재요청"); Main.ReqMarketData(MKList[req]); // req 인덱스에 해당하는 종목코드를 재요청 } } // ========================================================= // 6. 종목 검색 결과가 도착했을 때 실행되는 함수: Main_OnRcvItemList(aItemList, nCount) // ========================================================= function Main_OnRcvItemList(aItemList, nCount) { var d = new Date(); HHMMSS = d.getHours()*10000 + d.getMinutes()*100 + d.getSeconds(); Main.KillTimer(1); // 1번 타이머 중지 (검색 중복 방지) Main.MessageList(HHMMSS,"|종목검색완료|검색된종목수:", nCount); MKList = []; // 이전 검색 결과 초기화 if (nCount > 0) { // MAX_REQUEST_MARKET_DATA_COUNT에 따라 검색된 종목 수를 제한 var limitCount = Math.min(nCount, MAX_REQUEST_MARKET_DATA_COUNT); Main.MessageList(HHMMSS,"|매수 시도할 최대 종목 수:", limitCount); // 아직 매수된 종목객체(MK)가 전혀 없는 경우 if (MK.length == 0) { // 제한된 개수만큼 MKList에 저장 for (var i = 0; i < limitCount; i++) { MKList.push(aItemList[i]); } } else { // 이미 매수된 종목이 있을 경우, 중복되지 않는 종목 중 제한된 개수만 MKList에 추가 var addedCount = 0; for (var a = 0; a < nCount; a++) { if (addedCount >= limitCount) break; // 설정된 개수만큼 추가했으면 중단 var Add = true; for (var b = 0; b < MK.length; b++) { if (aItemList[a] == MK[b].code) // 이미 MK에 있는 종목이면 { Add = false; break; } } if (Add == true) { MKList.push(aItemList[a]); addedCount++; } } } Main.MessageList(HHMMSS,"|요청할 종목객체수 (필터링 후):",MKList.length); } // 새로 요청할 종목이 없다면, 1번 타이머 재설정 (다음 검색 대기) if (MKList.length == 0) { Main.SetTimer(1, timerInterval*1000); } else // 새로 요청할 종목이 있으면 MarketData 요청 시작 { req = 0; // 요청 인덱스 초기화 Main.MessageList(HHMMSS,"|종목객체요청",MKList[req]); Main.ReqMarketData(MKList[req]); // 실제 종목 객체 생성 요청 } } // ========================================================= // 7. 종목 객체(실시간 데이터)가 도착했을 때 실행되는 함수: Main_OnRcvMarketData(MarketData) // ========================================================= function Main_OnRcvMarketData(MarketData) { var d = new Date(); HHMMSS = d.getHours()*10000 + d.getMinutes()*100 + d.getSeconds(); if (MarketData.code == MKList[req]) // 수신된 MarketData 객체의 code가 현재 요청 중인 종목과 같다면 { Main.MessageList(HHMMSS,"|종목객체수신:",MarketData.code); // --- 실시간 데이터 및 이전 데이터 추출 --- var C = MarketData.Close[0]; // 현재 종가 var H = MarketData.High[0]; // 현재 고가 var L = MarketData.Low[0]; // 현재 저가 var O = MarketData.Open[0]; // 현재 시가 var V = MarketData.Volume[0]; // 현재 거래량 var prev_C = MarketData.Close[1]; // 1봉 전 종가 var prev_H = MarketData.High[1]; // 1봉 전 고가 var prev_L = MarketData.Low[1]; // 1봉 전 저가 var prev_O = MarketData.Open[1]; // 1봉 전 시가 var prev_V = MarketData.Volume[1]; // 1봉 전 거래량 // 필요한 기간만큼의 과거 데이터 추출 var closes = getRecentData(MarketData.Close, Math.max(BBPeriod, MAPeriod, CumulativePeriod, 5) + 2); var highs = getRecentData(MarketData.High, Math.max(MAPeriod, 5) + 2); var prev_closes = closes.slice(1); // 1봉 전부터 시작하는 종가 배열 var prev_highs = highs.slice(1); // 1봉 전부터 시작하는 고가 배열 // 데이터 부족 시 지표 계산 및 매매 로직 스킵 if (closes.length < Math.max(BBPeriod, MAPeriod) || highs.length < Math.max(MAPeriod, 5) || prev_closes.length < Math.max(BBPeriod, MAPeriod) || prev_highs.length < Math.max(MAPeriod, 5)) { Main.MessageList(HHMMSS, "|데이터 부족 (지표 계산 및 매매 스킵)", MarketData.code); // 데이터가 충분하지 않으면 다음 종목 요청으로 넘어감 MK.push(MarketData); req = req + 1; if (req < MKList.length) { Main.MessageList(HHMMSS,"|종목객체요청",MKList[req]); var S = Main.ReqMarketData(MKList[req]); if (S == -1) { Main.MessageList(HHMMSS,"|종목객체생성 15초제한"); Main.SetTimer(2, 15000); } } else { Main.MessageList(HHMMSS,"|종목객체생성끝"); Main.SetTimer(1, timerInterval*1000); } return; } // --- 예스트레이더 계산 섹션 로직 --- 기준 = (H + L + O) / 3 * V / 100000000; // 기준 값 계산 // 볼린저밴드 및 이동평균선 계산 MidLine = calculateMA(closes, BBPeriod); StdDevVal = calculateSTD(closes, BBPeriod); UpLine = MidLine + (StdDevVal * BBMul); DnLine = MidLine - (StdDevVal * BBMul); MA20 = calculateMA(closes, MAPeriod); MA20_High = calculateMA(highs, MAPeriod); MA05_High = calculateMA(highs, 5); // 이전 봉 지표 값 계산 var prev_MidLine = calculateMA(prev_closes, BBPeriod); var prev_DnLine = prev_MidLine - (calculateSTD(prev_closes, BBPeriod) * BBMul); var prev_UpLine = prev_MidLine + (calculateSTD(prev_closes, BBPeriod) * BBMul); var prev_MA20 = calculateMA(prev_closes, MAPeriod); var prev_MA20_High = calculateMA(prev_highs, MAPeriod); var prev_MA05_High = calculateMA(prev_highs, 5); // 볼린저밴드 폭과 추세 계산 BBWidth = UpLine - DnLine; BBWidth_Prev = prev_UpLine - prev_DnLine; // BBExpanded 및 BBContracted 상태 업데이트 (계단식) var currentBBExpanded = BBExpanded; // 현재 봉의 상태를 임시 저장 var currentBBContracted = BBContracted; if (BBWidth > BBWidth_Prev) { currentBBExpanded = true; currentBBContracted = false; } else if (BBWidth < BBWidth_Prev) { currentBBExpanded = false; currentBBContracted = true; } // else 문 없음: 이전 상태 유지 (기존 값 유지) BBExpanded = currentBBExpanded; // 전역 변수 업데이트 BBContracted = currentBBContracted; // 플래그 변수 업데이트: MA05_High와 MA20_High (계단식) var currentCrossUpFlag_MA05_MA20 = CrossUpFlag_MA05_MA20; var currentCrossDownFlag_MA05_MA20 = CrossDownFlag_MA05_MA20; if (isCrossUp(MA05_High, prev_MA05_High, MA20_High, prev_MA20_High)) { currentCrossUpFlag_MA05_MA20 = true; currentCrossDownFlag_MA05_MA20 = false; } else if (isCrossDown(MA05_High, prev_MA05_High, MA20_High, prev_MA20_High)) { currentCrossUpFlag_MA05_MA20 = false; currentCrossDownFlag_MA05_MA20 = true; } CrossUpFlag_MA05_MA20 = currentCrossUpFlag_MA05_MA20; CrossDownFlag_MA05_MA20 = currentCrossDownFlag_MA05_MA20; // 플래그 변수 업데이트: 종가(C)와 DnLine (계단식) var currentCrossUpFlag_C_DnLine = CrossUpFlag_C_DnLine; if (prev_H < prev_DnLine && isCrossUp(C, prev_C, DnLine, prev_DnLine)) { currentCrossUpFlag_C_DnLine = true; } else { // 조건 불만족시 초기화 (예스트레이더 동작 확인 필요) currentCrossUpFlag_C_DnLine = false; } CrossUpFlag_C_DnLine = currentCrossUpFlag_C_DnLine; // 20MA 상향 돌파 시점 종가 누적 계산을 위한 값 설정 (계단식) var currentCrossUpOccurredClose = CrossUpOccurredClose; // 이전 값 유지 if (prev_H < prev_MA20 && isCrossUp(C, prev_C, MA20, prev_MA20)) { currentCrossUpOccurredClose = C; } else { currentCrossUpOccurredClose = 0; // 돌파가 없으면 0으로 리셋 (예스트레이더 로직 따름) } CrossUpOccurredClose = currentCrossUpOccurredClose; // 누적 평균선 계산 (이 부분의 구현이 MarketData 과거 데이터 접근 방식에 따라 가장 크게 달라질 수 있습니다.) // 가장 안정적인 방법은 별도의 배열에 MarketData.Close[i]와 MarketData.MA20[i]를 저장하고 직접 순회하는 것입니다. var tempSumOfCloses = 0; var tempCrossUpCount = 0; // 임시 로직: 최근 CumulativePeriod 봉을 돌면서 가상으로 CrossUpOccurredClose 값을 추적 // 실제 구현에서는 MarketData.Close[i]와 MarketData.MA20[i]의 관계를 정확히 봐야 함. for (var i = 0; i < CumulativePeriod; i++) { // 이 부분은 예스스팟의 MarketData 객체가 과거 지표 값을 제공하는 방식에 따라 // 매우 정교한 구현이 필요합니다. 현재는 단순화된 형태입니다. // 예: if (MarketData.GetNthData(i+1, "High") < MarketData.GetNthData(i+1, "MA20") && // isCrossUp(MarketData.GetNthData(i, "Close"), MarketData.GetNthData(i+1, "Close"), // MarketData.GetNthData(i, "MA20"), MarketData.GetNthData(i+1, "MA20"))) { // tempSumOfCloses += MarketData.GetNthData(i, "Close"); // tempCrossUpCount += 1; // } } if (tempCrossUpCount > 0) { CumulativeAvg = tempSumOfCloses / tempCrossUpCount; } else { CumulativeAvg = 0; } // 볼린저밴드 하단선 상향 돌파 시 종가 추적 라인 계산 (계단식) var currentCrossUp_DnLine_추적 = CrossUp_DnLine_추적; if (prev_H < prev_DnLine && isCrossUp(C, prev_C, DnLine, prev_DnLine)) { currentCrossUp_DnLine_추적 = C; } else { // 조건 불만족시 초기화 (예스트레이더 동작 확인 필요) currentCrossUp_DnLine_추적 = 0; } CrossUp_DnLine_추적 = currentCrossUp_DnLine_추적; // 기준봉 신호 발생 조건 (계단식) var prev_기준 = (MarketData.High[1] + MarketData.Low[1] + MarketData.Open[1]) / 3 * MarketData.Volume[1] / 100000000; var prev_prev_기준 = (MarketData.High[2] + MarketData.Low[2] + MarketData.Open[2]) / 3 * MarketData.Volume[2] / 100000000; 기준봉_신호발생 = (기준 >= 1) && (O < C) && (C - O > (H - C) * 1.2) && (기준 >= ((prev_기준 + prev_prev_기준) / 2) * 3); // 기준선 설정 (계단식) var current_기준_고가 = 기준_고가; var current_기준_중심 = 기준_중심; var current_기준_저가 = 기준_저가; if (기준봉_신호발생) { current_기준_고가 = H; current_기준_중심 = (H + L) / 2; current_기준_저가 = L; } 기준_고가 = current_기준_고가; 기준_중심 = current_기준_중심; 기준_저가 = current_기준_저가; // 하락 신호 처리 (계단식) // Signal1Occurred는 매수Type_2에서 true로 설정되어야 함. var currentSignal1Occurred = Signal1Occurred; var currentDecline_Low = Decline_Low; if (currentSignal1Occurred && isCrossDown(C, prev_C, MA20, prev_MA20)) { currentSignal1Occurred = false; currentDecline_Low = L; } else { // 조건 불만족시 초기화 (예스트레이더 동작 확인 필요) currentDecline_Low = 0; } Signal1Occurred = currentSignal1Occurred; Decline_Low = currentDecline_Low; // --- 매수 조건 체크 및 주문 실행 --- var current_shares = Account1.GetPosition(MarketData.code).Amount; // 현재 보유 수량 var current_price_ask3 = MarketData.Ask(3); // 매수 시 호가 (매도3호가) // 전체 매매 활성화 및 일일 거래 횟수 제한 확인 if (ENABLE_TRADING && dailyTradeCount < MAX_TRADES_PER_DAY) { // 매수Type_1: 볼린저 하단선 상향 돌파 if (USE_BUY_TYPE1_DNLINE && current_shares == 0 && prev_H < prev_DnLine && isCrossUp(C, prev_C, DnLine, prev_DnLine)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type1", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_2: 고가 5일MA가 고가 20일MA 상향 돌파 if (USE_BUY_TYPE2_MA_HIGH_CROSS && current_shares == 0 && prev_MA05_High < prev_MA20_High && isCrossUp(MA05_High, prev_MA05_High, MA20_High, prev_MA20_High)) { Signal1Occurred = true; // 체크 신호 발생 플래그 설정 Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type2", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_3: 종가 고가 5일MA 상향 돌파 AND 종가 CumulativeAvg 아래 if (USE_BUY_TYPE3_C_MA05_HIGH && current_shares == 0 && prev_H < prev_MA05_High && isCrossUp(C, prev_C, MA05_High, prev_MA05_High) && (C < CumulativeAvg) && CumulativeAvg > 0) { // CumulativeAvg가 0이면 조건 충족 안함 Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type3", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_4: 종가 고가 20일MA & 미드라인 상향 돌파 if (USE_BUY_TYPE4_C_MA20H_MIDL && current_shares == 0 && prev_H < prev_MA20_High && prev_H < prev_MidLine && isCrossUp(C, prev_C, MA20_High, prev_MA20_High) && isCrossUp(C, prev_C, MidLine, prev_MidLine)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type4", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_5: 일단 (하락라인 돌파) if (USE_BUY_TYPE5_ONCE && current_shares == 0 && CrossDownFlag_MA05_MA20 && (C < CumulativeAvg) && CumulativeAvg > 0 && isCrossUp(C, prev_C, CrossUp_DnLine_추적, CrossUp_DnLine_추적)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type5", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_6: 현재가 UpLine 위에 있고, 이전 기준고가 돌파 if (USE_BUY_TYPE6_C_UP_기준고가 && current_shares == 0 && (C > UpLine) && isCrossUp(C, prev_C, 기준_고가, 기준_고가)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type6", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_7: 볼린저밴드 상단선 상향 돌파 var currentThriceSignalHigh = ThriceSignalHigh; // 현재 봉의 ThriceSignalHigh 임시 저장 if (USE_BUY_TYPE7_THRICE && current_shares == 0 && prev_H < prev_UpLine && isCrossUp(C, prev_C, UpLine, prev_UpLine)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type7", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); currentThriceSignalHigh = H; // 삼단 신호 발생 시 현재 봉의 고가 저장 dailyTradeCount++; } ThriceSignalHigh = currentThriceSignalHigh; // 전역 변수 업데이트 // 매수Type_8: 누적평균선 상향 돌파 if (USE_BUY_TYPE8_TWICE_CUM_AVG && current_shares == 0 && CumulativeAvg > 0 && prev_H < CumulativeAvg && isCrossUp(C, prev_C, CumulativeAvg, CumulativeAvg)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type8", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } // 매수Type_9: BB 수축 후 상단선 돌파 if (USE_BUY_TYPE9_BB_CONTRACTION_BREAKOUT && current_shares == 0 && BBExpanded && BBContracted && prev_H < prev_UpLine && isCrossUp(C, prev_C, UpLine, prev_UpLine)) { Account1.OrderBuy(MarketData.code, Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), current_price_ask3, 0); Main.MessageList(HHMMSS, "|매수주문-Type9", MarketData.code, "수량: " + Math.floor(ONE_TIME_BUY_AMOUNT / current_price_ask3), "가격: " + current_price_ask3); dailyTradeCount++; } } // --- 매도 조건 체크 및 주문 실행 --- var current_shares_sell = Account1.GetPosition(MarketData.code).Amount; // 현재 보유 수량 var current_price_bid3 = MarketData.Bid(3); // 매도 시 호가 (매수3호가) var avg_entry_price = Account1.GetPosition(MarketData.code).AvgPrice; // 평균 매수 단가 if (ENABLE_TRADING && dailyTradeCount < MAX_TRADES_PER_DAY && current_shares_sell > 0) { // 보유 수량이 있고, 거래 제한 내에서만 매도 조건 검토 // 매도Type_1: 삼단 고가 위에서 MA05_High 또는 UpLine 하향 돌파 시 전량 매도 if (USE_SELL_TYPE1_THRICE_BREAKDOWN && ThriceSignalHigh > 0 && prev_C > ThriceSignalHigh) { // 이전 봉 종가가 삼단 고가 위에 있었는지 if ((C > UpLine) && (isCrossDown(C, prev_C, MA05_High, prev_MA05_High) || isCrossDown(C, prev_C, UpLine, prev_UpLine))) { Account1.OrderSell(MarketData.code, current_shares_sell, current_price_bid3, 0); // 전량 매도 Main.MessageList(HHMMSS, "|매도주문-Type1", MarketData.code, "수량: " + current_shares_sell, "가격: " + current_price_bid3); var tradeProfitLoss = (current_price_bid3 - avg_entry_price) * current_shares_sell; Main.MessageList(HHMMSS, "|매도Type1 수익/손실:", tradeProfitLoss.toFixed(0) + "원"); dailyTradeCount++; } } // 매도Type_2: 갱신된 기준고가 라인 하향 돌파 시 매도 if (USE_SELL_TYPE2_KIJUN_HIGH_BREAKDOWN && C < 기준_고가) { Account1.OrderSell(MarketData.code, current_shares_sell, current_price_bid3, 0); // 전량 매도 Main.MessageList(HHMMSS, "|매도주문-Type2", MarketData.code, "수량: " + current_shares_sell, "가격: " + current_price_bid3); var tradeProfitLoss = (current_price_bid3 - avg_entry_price) * current_shares_sell; Main.MessageList(HHMMSS, "|매도Type2 수익/손실:", tradeProfitLoss.toFixed(0) + "원"); dailyTradeCount++; } } // --- 잔고 확인 및 로그 출력 --- var totalBalance = Account1.Balance; // 현재 예수금 (주문 가능 금액) var totalEvaluationProfitLoss = 0; var positions = Account1.GetPositionList(); // 현재 보유 종목 리스트 for (var i = 0; i < positions.length; i++) { var pos = positions[i]; // 현재 종목이 MarketData.code와 일치하는 경우의 현재가 사용, 아니면 GetPrice 등으로 최신가 가져와야 함. // 여기서는 단순화하여 현재 MarketData.code의 현재가를 사용 (모든 보유 종목에 대해 정확하진 않을 수 있음) var currentPosPrice = (pos.code == MarketData.code) ? C : Account1.GetPrice(pos.code).Current; totalEvaluationProfitLoss += (currentPosPrice - pos.AvgPrice) * pos.Amount; } Main.MessageList(HHMMSS, "|현재 잔고 | 예수금: " + totalBalance.toFixed(0) + "원 | 총 평가손익: " + totalEvaluationProfitLoss.toFixed(0) + "원"); // --- 다음 종목 객체 요청 로직 --- MK.push(MarketData); // 해당 종목의 MarketData 객체를 MK 배열에 추가 req = req + 1; // 다음 종목 요청 인덱스를 1 증가 // MAX_REQUEST_MARKET_DATA_COUNT를 초과하지 않고, 아직 요청할 종목이 남아 있다면 if (req < MKList.length && req < MAX_REQUEST_MARKET_DATA_COUNT) { Main.MessageList(HHMMSS,"|다음 종목객체요청",MKList[req]); var S = Main.ReqMarketData(MKList[req]); // 다음 종목 요청 // 15초에 60번 이상 요청하면 시간제한(-1) 받을 수 있음 if (S == -1) { Main.MessageList(HHMMSS,"|종목객체생성 15초제한. 잠시 후 재시도."); Main.SetTimer(2, 15000); // 제한에 걸리면 2번 타이머를 15초 후로 설정하여 재시도 } } else // 모든 요청을 끝냈거나 설정된 최대 개수를 초과했을 때 { Main.MessageList(HHMMSS,"|모든 종목객체 생성 완료 또는 최대 요청 개수 도달. 다음 검색 대기."); Main.SetTimer(1, timerInterval*1000); // 다시 1번 타이머(검색)를 5초 간격으로 재시작 } } } // ========================================================= // 8. 체결 정보 수신 시 실행되는 함수: Main_OnRcvChegyul(Chegyul) // ========================================================= function Main_OnRcvChegyul(Chegyul) { // 체결 정보 로그 출력 (예: 매수/매도, 종목코드, 수량, 가격 등) Main.MessageList(HHMMSS, "|체결알림:", Chegyul.code, Chegyul.gubun == "1" ? "매수" : "매도", Chegyul.qty, "주", Chegyul.price, "원"); // 이 함수 내에서 별도로 보유 수량이나 평균 단가를 업데이트할 필요는 없습니다. // Account1.GetPosition()을 통해 실시간으로 최신 정보를 조회할 수 있습니다. } 안녕하세요 고생이많으십니다!! 위의 코드는 제미나이랑 같이만든건대요 스팟실행시 구동되고 종목검색 되고 매수종목선정후 매수준비까지 하는데 매수가안되고 계속 검색 반복합니다 계좌연결 확인했고 별다른 오류 메세지는 안뜨는 상황이고요 처음 실행시 약3-5분간 데이터객체 머라고 뜨다가 검색 시작하고요 번거로우시겠지만 검증 및 수정 부탁드립니다!! 그럼 수고하세요!!
프로필 이미지
김범석
168
글번호 226326
답변완료

사용자함수

사용자함수를 만들고 지표를 만들려고하는데 어떻게 해야하나요??
프로필 이미지
윤호석
138
글번호 226322
답변완료

예스스팟 전략 작동 시간 문의

이번에 야간이 추가되었다고 해서 야간 데이터 확인용으로 전략을 만들었는데 18:00 ~ 02:45 까지만 확인이 됩니다. 혹시 전략 사용시간에 제한 같은것이 있는지 궁금합니다.
프로필 이미지
이스데일
153
글번호 226321

골드33 님에 의해서 삭제되었습니다.

프로필 이미지
골드33
0
글번호 226320
답변완료

분할매도 수식 문의

안녕하세요? 현재 예스랭귀지 종목검색을 기반으로 예스스팟에서 자동매수를 하고 수동 매도를 하고 있습니다. 혹시, 매수 후 특정한 조건에서 매도를 할 수 있게 수식 부탁드립니다.(스팟에 추가) 1. 추가할 매도 조건 a. 5% 수익일때 50% 매도(1차 익절) b. 1차 매도 후 2% 도달하면 전량 매도 c. 10% 수익일때 잔량의 50% 매도(2차 익절) d. 10% 이후 고점에서 5% 하락시 2차 익절후 잔량 전부 스탑트레일링 전량 매도 e. 손절 -5% 이렇게 매도식을 추가 하고 싶습니다.(%조건은 수정가능) 매도식을 현재 있는 자동매매 스팟 제일 아래 붙여넣어야 하나요? 감사합니다.
프로필 이미지
리버피닉스
182
글번호 226319

골드33 님에 의해서 삭제되었습니다.

프로필 이미지
골드33
2
글번호 226318
답변완료

지정가 매도 수식 부탁드립니다

Account1.SetBalance(i); if (Account1.Balance.count > 0) { Account1.OrderSell(Account1.Balance.code,Account1.Balance.count,0,1); } 위의 시장가 매도주문을 지정가(매도1호가 & 매수1호가)매도로 부탁드립니다. 감사합니다.
프로필 이미지
강자친구
146
글번호 226317

골드33 님에 의해서 삭제되었습니다.

프로필 이미지
골드33
13
글번호 226316
답변완료

문의드립니다.

9시10분에서20분까지 검색해서, 종목이 나오면 시장가 매수하고, 25분에 미체결이 있으면 취소하고, 30분에 잔고가 있으면 전량 시장가매도하는 건데... 잘 안됩니다... 어디가 문제일까요??? 프로그램이 문제일까...아님 뭐가 문제일까요??? 확인 부탁드립니다... 감사합니다...ㅎㅎㅎ var timer5 = 9; // 9초 간격 var OrderList = []; var MKList = []; var req; function Main_OnStart() { // 1번 타이머로 검색 시작 Main.SetTimer(1, timer5 * 1000); MKList = []; // 99번 타이머는 5초마다 09:30:00 여부 확인 Main.SetTimer(99, 5000); // 55번 타이머는 5초 간격으로 작동 Main.SetTimer(55, 5000); } function Main_OnTimer(nEventID) { var d = new Date(); var HHMMSS = d.getHours() * 10000 + d.getMinutes() * 100 + d.getSeconds(); if (nEventID == 1 && HHMMSS >= 91000 && HHMMSS <= 92000) { Main.ReqPowerSearch("매수조건"); } if (nEventID == 2) { Main.ReqMarketData(OrderList[req]); } if (nEventID == 99 && HHMMSS >= 93000) { Main.KillTimer(99); // 한 번만 실행 var num = Account1.GetTheNumberOfBalances(); if (num > 0) { for (var i = 0; i < num; i++) { Account1.SetBalanceIndex(i); if (Account1.Balance.count > 0) { Account1.OrderSell(Account1.Balance.code, Account1.Balance.count, 0, 1); Main.MessageList(Account1.Balance.code + " 주식을 전량 시장가로 매도합니다."); } } } } if (nEventID == 55 && HHMMSS >= 92500) { Main.KillTimer(55); Main.MessageLog("취소"); num = Account1.GetTheNumberOfUnfills(); // 미체결주문 갯수 for (var i = 0; i < num; i++) { Account1.SetUnfillIndex(i); // i순번의 미체결객체 셋팅 if (Account1.Unfill.count > 0) { // 미체결객체의 수량이 0이상이면 Account1.OrderCancel(Account1.Unfill.orderNum); // 해당주문번호 취소주문 } } } } function Main_OnRcvItemList(aItemList, nCount) { Main.KillTimer(1); OrderList = []; if (nCount >= 1) { if (MKList.length == 0) { OrderList = aItemList; } else { 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, 19, 0, 1); Main.MessageList(MarketData.code + " 주식을 19주 시장가로 매수합니다."); } else { Main.MessageList(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); } } } function IsStockInAccount(stockCode) { var numberOfBalances = Account1.GetTheNumberOfBalances(); for (var i = 0; i < numberOfBalances; i++) { Account1.SetBalance(i); if (stockCode == Account1.Balance.code) { return true; } } return false; }
프로필 이미지
시온사랑
152
글번호 226315