반응형
📌 개요
실시간 시세 데이터를 기반으로 자동화된 매매 전략을 구축할 때,
시그널 분석 로직과 실제 매수/매도 실행 로직을 별도로 분리하는 방식은 안정성과 유지보수 효율을 극대화할 수 있는 최선의 구조입니다.
이 글에서는 Excel VBA와 키움증권 DDE 연동 시스템을 기반으로,
실시간 조건 감지 → 매매 판단 → 수익률 분석까지 이어지는 분리 구조를 어떻게 구성해야 하는지 단계별로 설명합니다.
✅ 시스템 구성 핵심 구조
단계 | 함수이름 | 역활 |
① | 조건매매시그널_감지(r) | 실시간 조건 분석 및 텍스트 시그널 기록 |
② | 조건매매_매수매도판단(r) | 시그널 텍스트 기반으로 실제 매수/매도 실행 |
③ | 기록저장() | 위 2개 함수 통합 실행 (1초마다 반복 호출됨) |
🔍 ① 시그널 감지 함수 (조건매매시그널_감지)
🎯 목적:
실시간 조건에 따라 "매수 시그널", "매도 시그널", "거래량 시그널" 등 텍스트 형태로 판단만 기록
→ 이 텍스트를 기반으로 후속 판단 수행
✅ 예시 코드:
vba
Sub 조건매매시그널_감지(r As Long)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim currClose As Double: currClose = ws.Cells(r, 5).Value
Dim prevHigh As Double: prevHigh = ws.Cells(r - 1, 16).Value
Dim prevLow As Double: prevLow = ws.Cells(r - 1, 17).Value
Dim prevVols As Range: Set prevVols = ws.Range("I" & r - 5 & ":I" & r - 1)
Dim volAvg As Double: volAvg = WorksheetFunction.Average(prevVols)
Dim currVol As Double: currVol = ws.Cells(r, 9).Value
If currClose > prevHigh Then
ws.Cells(r, 50).Value = "📥 매수 시그널 - 고가 돌파"
ElseIf currClose < prevLow Then
ws.Cells(r, 50).Value = "📤 매도 시그널 - 저가 이탈"
ElseIf currVol > volAvg * 2 Then
ws.Cells(r, 50).Value = "💥 변동 시그널 - 거래량 급증"
End If
End Sub
🔍 ② 매매 판단 함수 (조건매매_매수매도판단)
🎯 목적:
위에서 기록된 시그널 텍스트(50열)를 읽어 실제 매수 또는 매도 실행 여부 판단 및 수익률 계산
✅ 예시 코드:
vba
Sub 조건매매_매수매도판단(r As Long)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim currClose As Double: currClose = ws.Cells(r, 5).Value
Dim signalText As String: signalText = ws.Cells(r, 50).Value
' 매수 시그널 발생 → 매수 실행
If signalText Like "*매수 시그널*" Then
ws.Cells(r, 51).Value = "📥 매수"
ws.Cells(r, 52).Value = currClose ' 매수가
' 매도 시그널 발생 → 보유 매수에 대해 익절/손절 판단
ElseIf ws.Cells(r - 5, 51).Value = "📥 매수" Then
Dim buyPrice As Double: buyPrice = ws.Cells(r - 5, 52).Value
If currClose >= buyPrice * 1.02 Then
ws.Cells(r, 51).Value = "📤 매도(익절)"
ws.Cells(r, 53).Value = currClose
ws.Cells(r, 54).Value = Format((currClose - buyPrice) / buyPrice, "0.00%")
ElseIf currClose <= buyPrice * 0.99 Then
ws.Cells(r, 51).Value = "📤 매도(손절)"
ws.Cells(r, 53).Value = currClose
ws.Cells(r, 54).Value = Format((currClose - buyPrice) / buyPrice, "0.00%")
End If
End If
End Sub
🔁 ③ 데이터 저장 메인 루틴 (기록저장)
이 함수에서는 실시간 데이터를 저장하고, 위 두 함수를 연동합니다.
vba
Sub 기록저장()
' ... 기존 데이터 저장 및 기술지표 계산 ...
' 시그널 감지 + 매매 판단 호출
조건매매시그널_감지 r
조건매매_매수매도판단 r
End Sub
📊 엑셀 열 구성 예시
열번호 | 항목 | 설명 |
50 | 조건 시그널 | 📥 매수 시그널 - 고가 돌파 등 |
51 | 매매 기록 | 📥 매수 / 📤 매도(익절) / 📤 매도(손절) |
52 | 매수가 | 시그널 발생 당시 매수가 |
53 | 매도가 | 청산 가격 |
54 | 수익률 | 계산된 수익률 % |
💡 이 구조의 장점
항목 | 장점 |
유지보수 | 시그널 조건과 매매 로직이 분리되어 조건 변경/추가가 쉬움 |
가독성 | 각 역할이 명확하게 나뉘어 있어 디버깅, 개선이 용이 |
확장성 | 시그널 종류를 늘리거나 백테스트 로직을 추가하기 쉬움 |
자동화 | 로그 기록, 수익률 분석, 실시간 시각화로 확장 가능 |
✅ 결론
엑셀 VBA 기반 자동 조건 매매 시스템을 설계할 때, 시그널 감지와 매매 실행을 분리하는 구조는 전략의 유연성과 코드 확장성을 확보하는 핵심입니다.
특히 실시간 데이터 기반 로직에서는 빠르게 신호만 감지하고 후속 행동을 분리 처리하는 것이 속도와 안정성을 높이는 최선의 전략입니다.
📘전체 Excel VBA 소스 코드
vba
' 모듈 전체: 실시간 수집 + 기술분석 계산 + 버튼 제어
Dim isRunning As Boolean
Dim startTime As Double
Dim nextRunTime As Date
Dim rStart As Long, rr As Long
Dim priceRange As Range, volRange As Range
Dim functionType As Variant
'시작
Sub 시작버튼_Click()
If isRunning Then Exit Sub
isRunning = True
Application.OnTime Now + TimeValue("00:00:01"), "기록반복"
End Sub
'중지
Sub 중지버튼_Click()
If isRunning Then
On Error Resume Next
Application.OnTime EarliestTime:=nextRunTime, Procedure:="기록반복", Schedule:=False
isRunning = False
MsgBox "수집이 중지되었습니다.", vbInformation
End If
End Sub
'초기화
Sub 초기화버튼_Click()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
If MsgBox("데이터를 초기화할까요?", vbYesNo + vbQuestion, "초기화 확인") = vbYes Then
ws.Rows("7:" & ws.Rows.Count).ClearContents
MsgBox "초기화 완료!"
End If
End Sub
'반복 호출
Sub 기록반복()
If Not isRunning Then Exit Sub
' 장 종료 여부 확인
If Time > TimeSerial(15, 45, 0) Then
isRunning = False
MsgBox "장이 종료되었습니다. 수집을 자동 중지합니다.", vbExclamation
Exit Sub
End If
' 실시간 데이터 저장
기록저장
' 다음 예약
nextRunTime = Now + TimeValue("00:00:01")
Application.OnTime EarliestTime:=nextRunTime, Procedure:="기록반복", Schedule:=True
End Sub
' 실시간 데이터 저장 + 기술지표 계산(이동평균선,볼린저밴드)
Sub 기록저장()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim r As Long: r = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row + 1
' 실시간 수집 (6행 기준 DDE 데이터 사용)
ws.Cells(r, 1).Value = "'" & Format(Now, "yyyy/mm/dd") ' 일자
ws.Cells(r, 2).Value = Format(Now, "hh:nn:ss") ' 시간
ws.Cells(r, 3).Value = ws.Range("C6").Value ' 종목코드
ws.Cells(r, 4).Value = ws.Range("D6").Value ' 종목명
ws.Cells(r, 5).Value = ws.Range("E6").Value ' 현재가
ws.Cells(r, 6).Value = ws.Range("F6").Value ' 기준가
ws.Cells(r, 7).Value = ws.Range("G6").Value ' 전일대비
ws.Cells(r, 8).Value = ws.Range("H6").Value ' 등락률
ws.Cells(r, 9).Value = ws.Range("I6").Value ' 거래량
ws.Cells(r, 10).Value = ws.Range("J6").Value ' 거래대금
ws.Cells(r, 11).Value = ws.Range("K6").Value ' 체결량
ws.Cells(r, 12).Value = ws.Range("L6").Value ' 체결강도
ws.Cells(r, 13).Value = ws.Range("M6").Value ' 상한가
ws.Cells(r, 14).Value = ws.Range("N6").Value ' 하한가
ws.Cells(r, 15).Value = ws.Range("O6").Value ' 시가
ws.Cells(r, 16).Value = ws.Range("P6").Value ' 고가
ws.Cells(r, 17).Value = ws.Range("Q6").Value ' 저가
ws.Cells(r, 18).Value = ws.Range("R6").Value ' 종가
ws.Cells(r, 19).Value = ws.Range("S6").Value ' 전일종가
ws.Cells(r, 20).Value = ws.Range("T6").Value ' 우선매도잔량
ws.Cells(r, 21).Value = ws.Range("U6").Value ' 우선매수잔량
ws.Cells(r, 22).Value = ws.Range("V6").Value ' 우선매도건수
ws.Cells(r, 23).Value = ws.Range("W6").Value ' 우선매수건수
ws.Cells(r, 24).Value = ws.Range("X6").Value ' 총매도잔량
ws.Cells(r, 25).Value = ws.Range("Y6").Value ' 총매수잔량
ws.Cells(r, 26).Value = ws.Range("Z6").Value ' 총매도건수
ws.Cells(r, 27).Value = ws.Range("AA6").Value ' 총매수건수
ws.Cells(r, 28).Value = ws.Range("AB6").Value ' 미결제약정
ws.Cells(r, 29).Value = ws.Range("AC6").Value ' 미결제전일대비
ws.Cells(r, 30).Value = ws.Range("AD6").Value ' 이론가
ws.Cells(r, 31).Value = ws.Range("AE6").Value ' 전일시가
ws.Cells(r, 32).Value = ws.Range("AF6").Value ' 전일고가
ws.Cells(r, 33).Value = ws.Range("AG6").Value ' 전일저가
' 기술 분석 (현재가: 열 6 기준)
Dim ma5 As Double, ma10 As Double, ma20 As Double, ma60 As Double, ma120 As Double
Dim bbAvg As Double, bbStd As Double
Dim hasEnoughData As Boolean: hasEnoughData = False
On Error Resume Next
If r >= 6 + 120 Then hasEnoughData = True
If Not hasEnoughData Then Exit Sub
On Error GoTo 0
' 이동평균선
ma5 = WorksheetFunction.Average(ws.Range("E" & r - 4 & ":E" & r))
ma10 = WorksheetFunction.Average(ws.Range("E" & r - 9 & ":E" & r))
ma20 = WorksheetFunction.Average(ws.Range("E" & r - 19 & ":E" & r))
ma60 = WorksheetFunction.Average(ws.Range("E" & r - 59 & ":E" & r))
ma120 = WorksheetFunction.Average(ws.Range("E" & r - 119 & ":E" & r))
' 볼린저 밴드
Set last20 = ws.Range("E" & r - 19 & ":E" & r)
bbStd = WorksheetFunction.StDev(last20)
bbAvg = ma20
' 결과 화면 디스플레이 (열 순서: 35 ~ 42)
ws.Cells(r, 35).Value = ma5
ws.Cells(r, 36).Value = ma10
ws.Cells(r, 37).Value = ma20
ws.Cells(r, 38).Value = ma60
ws.Cells(r, 39).Value = ma120
ws.Cells(r, 40).Value = bbAvg + 2 * bbStd
ws.Cells(r, 41).Value = bbAvg
ws.Cells(r, 42).Value = bbAvg - 2 * bbStd
' --- 봉 차트 계산 영역 추가 ---
Dim minuteNow As Date: minuteNow = Int(Now * 24 * 60) / (24 * 60)
' Helper function: 봉 생성 함수
For Each functionType In Array(1, 5, 10) ' 1분봉, 5분봉, 10분봉
rStart = 7
For rr = r - 1 To 7 Step -1
If ws.Cells(rr, 2).Value = "" Then Exit For
If IsDate(ws.Cells(rr, 2).Value) Then
If TimeValue(ws.Cells(rr, 1).Value) <= TimeSerial(Hour(Now), Minute(Now) - functionType, 0) Then
rStart = rr + 1
Exit For
End If
End If
Next rr
' 시가, 종가, 고가, 저가 계산
If r >= rStart Then
Set priceRange = ws.Range("E" & rStart & ":E" & r)
Set volRange = ws.Range("I" & rStart & ":I" & r)
Dim o As Double, h As Double, l As Double, c As Double, v As Double
o = ws.Cells(rStart, 5).Value
h = WorksheetFunction.Max(priceRange)
l = WorksheetFunction.Min(priceRange)
c = ws.Cells(r, 5).Value
v = WorksheetFunction.Sum(volRange)
' 결과 셀 위치: 1분봉(열 43), 5분봉(열 44), 10분봉(열 45)
Select Case functionType
Case 1: ws.Cells(r, 43).Value = c ' 1분봉 종가로 표시
Case 5: ws.Cells(r, 44).Value = c
Case 10: ws.Cells(r, 45).Value = c
End Select
End If
Next
' 조건 시그널 감지
조건매매시그널_감지 r
' 매수/매도 조건 처리 추가
조건매매_매수매도판단 r
End Sub
' 조건 시그널 감지 예시
Sub 조건매매시그널_감지(r As Long)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim currClose As Double: currClose = ws.Cells(r, 5).Value
Dim prevHigh As Double: prevHigh = ws.Cells(r - 1, 16).Value
Dim prevLow As Double: prevLow = ws.Cells(r - 1, 17).Value
Dim prevVols As Range: Set prevVols = ws.Range("I" & r - 5 & ":I" & r - 1)
Dim volAvg As Double: volAvg = WorksheetFunction.Average(prevVols)
Dim currVol As Double: currVol = ws.Cells(r, 9).Value
If currClose > prevHigh Then
ws.Cells(r, 50).Value = "📥 매수 시그널 - 고가 돌파"
ElseIf currClose < prevLow Then
ws.Cells(r, 50).Value = "📤 매도 시그널 - 저가 이탈"
ElseIf currVol > volAvg * 2 Then
ws.Cells(r, 50).Value = "💥 변동 시그널 - 거래량 급증"
End If
End Sub
Sub 조건매매시그널_감지(r As Long)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim currClose As Double: currClose = ws.Cells(r, 5).Value
Dim prevHigh As Double: prevHigh = ws.Cells(r - 1, 16).Value
Dim prevLow As Double: prevLow = ws.Cells(r - 1, 17).Value
Dim prevVols As Range: Set prevVols = ws.Range("I" & r - 5 & ":I" & r - 1)
Dim volAvg As Double: volAvg = WorksheetFunction.Average(prevVols)
Dim currVol As Double: currVol = ws.Cells(r, 9).Value
If currClose > prevHigh Then
ws.Cells(r, 50).Value = "?? 매수 시그널 - 고가 돌파"
ws.Cells(r, 51).Value = "?? 매수"
ws.Cells(r, 52).Value = currClose ' 매수가
ElseIf currClose < prevLow Then
ws.Cells(r, 50).Value = "?? 매도 시그널 - 저가 이탈"
ws.Cells(r, 51).Value = "?? 매도"
ws.Cells(r, 52).Value = currClose ' 매도가
ElseIf currVol > volAvg * 2 Then
ws.Cells(r, 50).Value = "?? 변동 시그널 - 거래량 급증"
'ws.Cells(r, 51).Value = "?? 매수"
'ws.Cells(r, 52).Value = currClose ' 매수가
End If
End Sub
Sub 조건매매_매수매도판단(r As Long)
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim currClose As Double: currClose = ws.Cells(r, 5).Value
Dim signalText As String: signalText = ws.Cells(r, 50).Value
' 매수 시그널 발생 → 매수 실행
If signalText Like "*매수 시그널*" Then
ws.Cells(r, 51).Value = "📥 매수"
ws.Cells(r, 52).Value = currClose ' 매수가
' 매도 시그널 발생 → 보유 매수에 대해 익절/손절 판단
ElseIf ws.Cells(r - 5, 51).Value = "📥 매수" Then
Dim buyPrice As Double: buyPrice = ws.Cells(r - 5, 52).Value
If currClose >= buyPrice * 1.02 Then
ws.Cells(r, 51).Value = "📤 매도(익절)"
ws.Cells(r, 53).Value = currClose
ws.Cells(r, 54).Value = Format((currClose - buyPrice) / buyPrice, "0.00%")
ElseIf currClose <= buyPrice * 0.99 Then
ws.Cells(r, 51).Value = "📤 매도(손절)"
ws.Cells(r, 53).Value = currClose
ws.Cells(r, 54).Value = Format((currClose - buyPrice) / buyPrice, "0.00%")
End If
End If
End Sub
📝 적용해 보시고 모르는것 있으시면 댓글 달아주세요. 확인해서 알려드릴께요 !!! (현재 코드 흐름은 "매수 먼저 → 이후 매도(익절/손절)"만 전제하고 있어, 공매도 시나리오나 매도 시그널 독립 감지는 빠져 있는 구조입니다.)
✅ 다음편에 매도 시그널 감지 부분 추가 하겠습니다.
반응형