📘 프로젝트 명:Excel VBA + 조건매매 자동화 시스템 – 매도 시그널 독립 감지

반응형

실제로 매도 시그널이 먼저 발생하는 경우는 매우 많고 중요합니다.
그런데 현재 코드 흐름은 "매수 먼저 → 이후 매도(익절/손절)"만 전제하고 있어, 공매도 시나리오매도 시그널 독립 감지는 빠져 있는 구조입니다.


✅ 현재 구조 요약 (한계)

현재 로직은 다음과 같이 구성되어 있습니다:

현재 흐름:

  1. 매수 시그널 감지 → 매수 기록 (📥 매수)
  2. 몇 틱 후 → 익절/손절 조건 감지 시 → 📤 매도(익절/손절)
  3. "매도 시그널"이 발생해도, 매수 포지션이 없으면 아무 반응 없음

❗문제점

조건문제 설명
📤 매도 시그널이 먼저 발생 하지만 보유 포지션이 없다고 가정되어 무시됨
공매도 전략 테스트 불가능 시뮬레이션 시 숏 포지션 조건 테스트 불가
백테스트 정확도 저하 조건식은 존재하지만 반응하지 않음

✅ 해결 방향: 매도 포지션도 "독립적"으로 감지하고 처리해야 함

🧠 목표:

  • 📤 매도 시그널이 먼저 나올 수 있다 → 그때는 매도 진입 (숏 포지션)
  • 이후 가격이 내려가면 익절, 올라가면 손절 (반대 로직)

✅ 개선된 매수/매도 감지 판단 구조

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

    ' 📉 숏포지션 기준 익절/손절 판별
    ElseIf ws.Cells(r - 5, 51).Value = "📤 공매도" Then
        Dim sellPrice As Double: sellPrice = ws.Cells(r - 5, 52).Value
        If currClose <= sellPrice * 0.98 Then
            ws.Cells(r, 51).Value = "📥 환매수(익절)"
            ws.Cells(r, 53).Value = currClose
            ws.Cells(r, 54).Value = Format((sellPrice - currClose) / sellPrice, "0.00%")
        ElseIf currClose >= sellPrice * 1.01 Then
            ws.Cells(r, 51).Value = "📥 환매수(손절)"
            ws.Cells(r, 53).Value = currClose
            ws.Cells(r, 54).Value = Format((sellPrice - currClose) / sellPrice, "0.00%")
        End If
    End If
End Sub

📊 엑셀 열 확장 예시

설명
50 조건 시그널 (📥 매수 시그널 - 고가 돌파 등)
51 포지션 상태 (📥 매수, 📤 공매도, 📤 매도(익절), 📥 환매수(익절) 등)
52 진입가
53 청산가
54 수익률

✅ 정리

시나리오 동작
매수 시그널 → 매수 → 익절/손절 기존 구조 그대로
매도 시그널 → 공매도 → 익절/손절 ✨ 추가된 개선 구조로 처리 가능

📘전체 Excel VBA 소스 코드

Source Code Icon

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
    
    ' ? 매수/매도 조건 처리 추가
    조건매매_매수매도판단 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 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

    ' ?? 숏포지션 기준 익절/손절 판별
    ElseIf ws.Cells(r - 5, 51).Value = "?? 매도" Then
        Dim sellPrice As Double: sellPrice = ws.Cells(r - 5, 52).Value
        If currClose <= sellPrice * 0.98 Then
            ws.Cells(r, 51).Value = "?? 환매수(익절)"
            ws.Cells(r, 53).Value = currClose
            ws.Cells(r, 54).Value = Format((sellPrice - currClose) / sellPrice, "0.00%")
        ElseIf currClose >= sellPrice * 1.01 Then
            ws.Cells(r, 51).Value = "?? 환매수(손절)"
            ws.Cells(r, 53).Value = currClose
            ws.Cells(r, 54).Value = Format((sellPrice - currClose) / sellPrice, "0.00%")
        End If
    End If
End Sub
반응형