2013년 5월 30일 목요일

2013년 4월 1일 월요일

Introducing Elliott Pattern Helper-DDE

Elliott Pattern Helper-DDE

Elliott Pattern Helper-DDE (EPH-DDE) is an Excel Add-In, which utilizes Elliott monowave analysis functionality of EPH and DDE of various home trading softwares.

It helps users accumulating candlestick informations from quotes incoming through DDE links, analyzing Elliott wave structures of monowaves and waves in between adjacent fractal start and fractal signal, catching some buy/sell signals based on the results of EPH analyses, finally getting trading edges.



▶  Main functionality
  • DDE programs and items management
  • DDE channel management
  • Candle feeder
  • Technical Indicators
    • MACD, RSI, OBV, CCI, Stochastic, Disparity
    • Fractal Count
    • Bill Williams' Profitunity Window
  • Elliott wave pattern analysis (with help of EPH)
    • Monowaves
    • Elliott wave fractals
    • Polywaves in between fractal start and fractal signal
  • Signals
    • Indicator primitive signals 
    • User-defined signals (combinations of primitive signals)
  • Charting


▶  Screenshots
  • EPH-DDE is an Excel Add-In

Figure 1. Excel add-in

  • Selecting indicators when scheduling

Figure 2. Indicators


  • Candlestick Charts with an indicator
    • Toggle button toggles display mode
    • display mode: fractal count, monowave patterns, fractal start-signal mode

Figure 3. Chart sheet


  • Monowave Analysis

Figure 4. Elliott patterns of each monowave

  • Fractal Start-Signal Analysis

Figure 5. Elliott wave patterns of fractal start-signal

  • Setting up user-defined signals

Figure 6. Defining user-defined signal conditions


  • Buy/Sell Signal

Figure 7. Signals

 

2013년 3월 21일 목요일

Excel에서 HTS DDE 활용하기 5 - Chart와 지표 VIII

※ Elliott Pattern Helper Add In
  • Download Add In for Excel 2007 
  • Download Add In for Excel 2003 

  'Excel에서 HTS DDE 활용하기 5' 시리즈의 마지막 글로서, 지표를 활용하여 신호를 포착하고 표시하는 방법에 관한 주제를 다루겠습니다.

 
  기술적 지표는, 사용하는 사람들마다 나름대로의 목적이 있겠지만, 지정한 조건에 따라 신호를 발생시켜 매수 또는 매도의 시점을 찾는 것이
중요한 용도 중 하나일 것입니다.
  Elliott oscillator MACD(5, 35, 5)를 예를 들어 볼까요?

  • 5일 이동평균이 35일 이동평균을 상향돌파(golden cross)할 때와 하향돌파(dead cross)할 때를 각각 매수와 매도시점으로 정한다. 이는 각각 oscillator가 (-)에서 (+)로 변할 때, (+)에서 (-)에서 변할 때이다. 
  • Oscillator가 그 이동평균(signal line)을 상향돌파할 때와 하향돌파할 때를 각각 매수 및 매도시점으로 정한다.
  주가와 지표 사이의 divergence를 활용할 수도 있을 것입니다.
  • Bullish divergence: 주가는 신저가를 형성하는데 지표는 그렇지 않은 경우
  • Bearish divergence: 주가는 신고가를 형성하지만 지표는 전고가보다 낮은 경우
  
  증권사에서 제공하는 HTS는 대부분 조건검색 기능을 제공하여 사용자가 지정한 조건에 따라 신호를 발생시키도록 할 수 있습니다. 그러므로 굳이 프로그래밍 하지 않고 HTS를 사용할 수도 있겠지만, HTS가 제공하지 않는 지표나 신호 포착을 위한 방법이 필요할 것입니다.  
  
  지난 글에서 완성된 파일()을 수정하여 몇몇 신호를 포착하고 정해진 시트에 출력하도록 하겠습니다. 

▶ 지표 클래스 수정  

 
먼저 현재까지 완성된 엑셀 VB 프로그램의 기본적인 작동 구조를 상기해 보겠습니다. DDE 연결 셀의 값이 변하면 'SetLinkOnData'로 지정된 함수를 통해 'CCandleFeeder' 오브젝트의 'store' 함수가 호출됩니다. 함수 'store'는 캔들정보를 축적함과 동시에 여러 지표 클래스의  허브(hub) 역할을 하는 'CStockIndicator' 클래스'updateLatest'를 호출하여 최종 지표값을 계산하도록 합니다.

  
  MACD의 이동평균 cross나 oscillatorsignal의 cross는 지표 자체만으로 포착할 수 있지만, divergence의 경우 fractal 지표와 MACD 또는 RSI 등 oscillator 지표를 함께 분석해야 포착할 수 있습니다. 어떤 경우이던 'CStockIndicator', 'CStockIndicatorRSI', 'CStockIndicatorFRTL' 등 지표를 실제계산하는 클래스에서 신호를 포착하도록 하는 것이 자연스러울 것입니다. 
  
  별도의 클래스로 작성하지 않았던 MACD 지표를 대상으로 클래스를 작성해 가면서 글을 풀어나가도록 하겠습니다. 'CStockIndicatorMACD' 클래스를 지난 과정에서 작성했던 'CStockIndicatorRSI'와 동일한 구조로 만들되 'updateLatest' 함수에서 신호를 발생시키도록 합니다.
  
  ※ (참고) 클래스 소스는 크게 네 부분으로 구성했습니다. 멤버 변수 역할을 하는 property 선언부는 소문자 p로 시작하는 변수들을 포함합니다. 그 밑에 property 이외에 오브젝트의 상태들을 저장하기 위한 변수들을 선언하는데, 이 부분은 클래스에 따라 없을 수도 있습니다. 세 번째 부분은 'Property Let' 또는 'Property Get'으로 시작되는 멤버변수 초기화 및 조회를 위한 함수들을 포함합니다. 마지막 네 번째 부분에 'init', 'updateLatest' 등 클래스 고유의 기능을 처리하는 함수들을 작성했습니다.
  
  ※ (참고) 'init'은 이미 축적된 모든 캔들에 대해 지표를 일괄 계산하는데, 캔들정보 축적시트 별로 지표 클래스의 오브젝트가 생성될 때 한 번 수행됩니다.
  
  지표 클래스의 'updateLatest' 함수는 DDE를 통해 수신한 체결정보를 반영하여 마지막 캔들의 지표를 다시 계산하기 위해 작성했었습니다. 이것을 수정하여 지표가 일정 기준을 충족시킬 때 신호(0보다 큰 정수값)를 return하도록 합니다. 아래 소스를 참고하십시요.  
  
  ※ (클래스 'CStockIndicatorMACD' 소스)
   
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Part 1. 멤버 변수
    Private pShtName As String        'candle sheet name
    Private pColC As Integer        'column of close
    Private pPeriodS As Integer        'short MA period
    Private pPeriodL As Integer        'long MA period
    Private pPeriodSig As Integer    'oscillator MA period
    Private pMA As ma_t                'MA type
    Private pHSig As Boolean        'whether to use oscillator MA or not
    Private pCol As Integer            'column of indicator

  
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Part 2. 기타 변수
    Private lrow As Integer            'last row
    ''''for signal capturing        ''''
    Private lval As Double            'last indicator value
    Private ldiff As Double            'last (oscillator MA - oscillator)
    Private lsig As Double            'last oscillator MA
    ''''to remove redundant signal    ''''
    Private ltu As Integer          'current time unit
    Private ltc As Integer          'last time unit of captured signal
    Private lsc As Integer          'last signal captured

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Part 3. Property 함수
    Property Let ShtName(v As String)
        If v = Empty Or v = xlNullString Then _
            Err.Raise 8921, "CStockIndicatorMACD.Let ShtName", "Invalid Argument"
        pShtName = v
    End Property

    Property Let ColC(v As Integer)
        If v < 0 Then _
            Err.Raise 8921, "CStockIndicatorMACD.Let ColC", "Invalid Argument"
        pColC = v
    End Property

    Property Let PeriodS(v As Integer)
        If v < 0 Then _
            Err.Raise 8921, "CStockIndicatorMACD.Let PeriodS", "Invalid Argument"
        pPeriodS = v
    End Property

    Property Let PeriodL(v As Integer)
        If v < 0 Then _
            Err.Raise 8921, "CStockIndicatorMACD.Let PeriodL", "Invalid Argument"
        pPeriodL = v
    End Property

    Property Let PeriodSig(v As Integer)
        If v < 0 Then _
            Err.Raise 8921, "CStockIndicatorMACD.Let PeriodSig", "Invalid Argument"
        pPeriodSig = v
    End Property

    Property Let MA(v As ma_t)
        pMA = v
    End Property

    Property Let Col(v As Integer)
        If v < 0 Then _
            Err.Raise 8921, "CStockIndicatorMACD.Let Col", "Invalid Argument"
        pCol = v
    End Property

    Property Let HSig(v As Boolean)
        pHSig = v
    End Property

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Part 4. 클래스 고유기능 처리 함수
    Function init() As Variant    ''''초기 일괄 계산
    Debug.Print "[CStockIndicatorMACD.init]s " & Timer
        Dim v As Variant, c() As Variant
        ltu = 0
      
        With Sheets(pShtName).UsedRange
            lrow = .Rows.Count
            nrow = lrow - 1  'header excluded
            ReDim c(1 To nrow)
            c = WorksheetFunction.Transpose(.Cells(2, pColC).Resize(nrow, 1))
        End With
      
        v = macd(c, pPeriodS, pPeriodL, pMA)
        init = v
    Debug.Print "[CStockIndicatorMACD.init]e " & Timer
    End Function

    Function updateLatest(b) As Integer    ''''DDE 이벤트 반영
        updateLatest = 0
        ''''updateLatest = 0 none, 1 (-) to (+), 2 (+) to (-), 4 sig GC, 8 sig DC
        ''''if needed, add another
      
        If b Then
            lrow = lrow + 1
            ltu = ltu + 1
        End If
      
        If lrow <= pPeriodL Then GoTo Continue:
        If b Or lval = 0 Then
            Sheets(pShtName).Cells(lrow, pCol).Value = _
                "=AVERAGE(" & _
                Cells(lrow - pPeriodS + 1, pColC).Resize(pPeriodS, 1).Address & ")" & _
                "-AVERAGE(" & _
                Cells(lrow - pPeriodL + 1, pColC).Resize(pPeriodL, 1).Address & ")"
            lval = Sheets(pShtName).Cells(lrow - 1, pCol).Value
            If pHSig Then lsig = Sheets(pShtName).Cells(lrow - 1, pCol + 1).Value
            ldiff = lsig - lval
        End If
      
        cval = Sheets(pShtName).Cells(lrow, pCol).Value
        If cval * lval < 0 Then
            If lval < 0 Then
                updateLatest = updateLatest Or 1            'MA GC?
            Else
                updateLatest = updateLatest Or 2            'MA DC?
            End If
        End If
      
        If pHSig Then
            If b Then
                If pMA = ma_s Then
                    Sheets(pShtName).Cells(lrow, pCol + 1).Value = "=AVERAGE(" _
                        & Cells(lrow - pPeriodSig + 1, pCol).Resize(pPeriodSig, 1).Address & ")"
                Else
                    Sheets(pShtName).Cells(lrow, pCol + 1).Value = _
                        "=2*(OFFSET(RC,0,-1)-OFFSET(RC,-1,0))/(1+" _
                        & pPeriodSig & ")+OFFSET(RC,-1,0)"
                End If
            End If
            lsig = Sheets(pShtName).Cells(lrow, pCol + 1).Value
            cdiff = lsig - cval
            If ldiff * cdiff < 0 Then
                If ldiff > 0 Then
                    updateLatest = updateLatest Or 4        'golden cross?
                Else
                    updateLatest = updateLatest Or 8        'dead cross?
                End If
            End If
        End If
      
        If updateLatest <> 0 Then
            If ltu <> ltc Or updateLatest <> lsc Then
                ltc = ltu
                lsc = updateLatest
            Else
                'Debug.Print "dropping redundant signal " & pShtName & " " & updateLatest
                updateLatest = 0
            End If
        End If
    Continue:
    End Function

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

  ☞ 짧은 기간의 이동평균이 긴 기간의 이동평균을 상향돌파할 때 1, 하향돌파할 때 2를 return합니다.

  ☞ Oscillator가 oscillator 이동평균(signal line)을 상향돌파할 때 4, 하향돌파할 때 8을 return합니다.
  
  OBV 지표도 MACD와 동일한 구조로 신규 작성하고, signal line의 golden cross와 dead cross 신호를 발생시키도록 합니다. 
  ※ 클래스 'CStockIndicatorOBV' 소스
  
  RSI 지표 클래스는 과매수, 과매도 구간 진입과 이탈 시 신호를 발생시키도록 수정합니다.
  ※ 수정된 클래스 'CStockIndicatorRSI' 소스
  
  PFTW 지표(Profitunity Window, Bill Williams) 클래스는 squat 캔들이 발생한 경우, green 캔들 직후에 squat 캔들이 발생한 경우 신호를 발생시키도록 수정합니다.
  ※ 수정된 클래스 'CStockIndicatorPFTW' 소스
  
  FRTL 지표(Elliott wave fractal, Bill Williams) 클래스는 up 또는 down fractal에서 MACD 지표 또는 RSI 지표의 divergence가 관측될 때, consolidation이 관측될 때 신호를 발생시키도록 수정합니다.
  ※ 수정된 클래스 'CStockIndicatorFRTL' 소스  
  
  지표 클래스의 추가 수정사항을 반영하여 'CStockIndicator' 클래스도 수정해 줍니다. 이때 각 지표 클래스의 'updateLatest' return값을 'CCandleFeeder'에게 전달하도록 하여 'CCandleFeeder'가 최종적으로 신호를 출력할 수 있도록 합니다.
  ※ 수정된 클래스 'CStockIndicator' 소스

▶ 'CCandleFeeder' 클래스 수정  

 
'CCandleFeeder'의 'store' 함수는 DDE를 통해 수신한 체결정보를 바탕으로 마지막 캔들을 update하거나 새로운 캔들을 생성하고 'CStockIndicator'의 'updateLatest'를 호출
하도록 작성되었습니다.
  
  이를 수정하여 신호가 발생되었을 때, 즉 'updateLatest'의 return값이 0보다 클 때 정해진 시트에 신호를 출력하도록 합니다. 아래 소스를 참고하십시요.  
  
  ※ (수정 후 'fillCandle()' 함수 소스)
  
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Function fillCandle(sh_name, l, d, p, v)
        With Sheets(sh_name)
            .Cells(l, 1).Value = d
            .Cells(l, 2).Value = p
            .Cells(l, 3).Value = p
            .Cells(l, 4).Value = p
            .Cells(l, 5).Value = p
            .Cells(l, 6).Value = v
        End With
       
        For Each indi In pTIndicator
            sigs = indi.updateLatest(l, True)
            'Debug.Print "sigs " & sigs
            If sigs > 0 Then Call sig_add(bt & "." & pCandleSheet & "." _
                                          & indi.Indicator & "." & sigs)
        Next
       
    End Function

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

  ☞ 앞으로 작성할 'sig_add' 함수를 호출합니다. 

  ☞ 'sig_add'는 정해진 시트에 신호가 발생된 시각, 종목, 캔들의 시간 단위, 지표 식별자 정수값 신호를 출력하도록 할 것입니다.
   
  ※ (수정 후 'updateCandle()' 함수 소스)
  
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Function updateCandle(sh_name, l, p, v)
        With Sheets(sh_name)
            If p > .Cells(l, 3).Value Then .Cells(l, 3).Value = p
            If p < .Cells(l, 4).Value Then .Cells(l, 4).Value = p
            .Cells(l, 5).Value = p
            .Cells(l, 6).Value = v
        End With
       
        For Each indi In pTIndicator
            sigs = indi.updateLatest(l, False)
            'Debug.Print "sigs " & sigs
            If sigs > 0 Then Call sig_add(bt & "." & pCandleSheet & "." _
                                          & indi.Indicator & "." & sigs)
        Next
       
    End Function

    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  
  ☞ 앞으로 작성할 'sig_add' 함수를 호출합니다. 
  ☞ 'sig_add'로 넘겨주는 문자열 인자에 포함된 변수 'bt'는 체결시간입니다. 클래스의 변수로 추가해야 합니다. 아래의 수정된 클래스 소스를 참고하십시요.
  
  ※ 수정된 클래스 'CCandleFeeder' 소스

▶ 신호 출력을 위한 함수 작성  

  지표 클래스가 발생시킨 신호를 시각적으로 볼 수 있어야 할 것입니다. 다양한 방법이 있겠지만, 가장 단순하고 쉬운 방법으로, 전용 시트를 생성하여 목록을 쌓도록 하겠습니다. 그리고 신호 목록을 모두 삭제하는 기능, 선택된 목록만을 삭제하는 기능, 신호 출력을 On/Off 하는 기능을 작성하겠습니다. 

  
  먼저 신호출력 시트를 생성하기 위한 함수 'createSigSheet'를 작성합니다. 이 함수는 'prepareSheet' 함수에서 호출하도록 할 것입니다.
  
  ※ (함수 'createSigSheet' 소스)
  
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Public Const SIGS_POSTFIX = "SIGNALS"   'Signals sheet name
    Private is_on As Boolean

    Function createSigSheet()
        Dim xlSheet As Worksheet
       
        If Not sheetExist(SIGS_POSTFIX) Then
            Set xlSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
            xlSheet.Name = SIGS_POSTFIX
            xlSheet.Cells(2, 1).Value = "Signals"
            Set btn = xlSheet.Buttons.Add(xlSheet.Cells(2, 1).Width * 1, _
                                          xlSheet.Cells(2, 1).Height, _
                                          xlSheet.Cells(2, 1).Width, _
                                          xlSheet.Cells(2, 1).Height)
            btn.Name = "ButtonToggle"
            btn.OnAction = "on_off_toggle"
            btn.Characters.Text = "On / Off"
            With btn.Characters.Font
                .Size = 10
            End With
           
            Set btn = xlSheet.Buttons.Add(xlSheet.Cells(2, 1).Width * 2, _
                                          xlSheet.Cells(2, 1).Height, _
                                          xlSheet.Cells(2, 1).Width, _
                                          xlSheet.Cells(2, 1).Height)
            btn.Name = "ButtonClear"
            btn.OnAction = "sigs_clear"
            btn.Characters.Text = "Clear"
            With btn.Characters.Font
                .Size = 10
            End With
           
            Set btn = xlSheet.Buttons.Add(xlSheet.Cells(2, 1).Width * 3, _
                                          xlSheet.Cells(2, 1).Height, _
                                          xlSheet.Cells(2, 1).Width, _
                                          xlSheet.Cells(2, 1).Height)
            btn.Name = "ButtonDelete"
            btn.OnAction = "sigs_delete"
            btn.Characters.Text = "Delete"
            With btn.Characters.Font
                .Size = 10
            End With
           
            lbwidth = xlSheet.Cells(2, 1).Width * 10
            Set lb = xlSheet.OLEObjects.Add(ClassType:="Forms.ListBox.1", _
                                            Link:=False, _
                                            DisplayAsIcon:=False, _
                                            Left:=xlSheet.Cells(2, 1).Width, _
                                            Top:=xlSheet.Cells(2, 1).Height * 3, _
                                            Width:=lbwidth, _
                                            Height:=xlSheet.Cells(2, 1).Height * 20)
            lb.Name = "SigsList"
            lb.Activate
            With lb.Object
                .Enabled = True
                .Locked = False
                .MultiSelect = 1
                .ListStyle = 1
                .ColumnCount = 4
                .ColumnWidths = 0.1 * lbwidth & ";" & 0.3 * lbwidth & ";" & _
                                0.1 * lbwidth & ";" & 0.5 * lbwidth
            End With
        Else
            is_on = True
        End If
       
        Set xlSheet = Nothing
    End Function
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''   

  ☞ 신호출력 시트가 생성될 때 'On/Off' 버튼, 'Clear' 버튼, 'Delete' 버튼 및 신호 목록을 출력하기 위한 목록 상자(ListBox) 생성되도록 습니다. 
   
  신호출력 시트에 생성한 세 개의 버튼에 각각 대응하는 함수를 작성해야 합니다. 아래 소스를 참고하십시요.
  
  ※ (버튼 이벤트 소스)
  
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Sub sigs_clear()
        Dim ws As Worksheet
        Dim lb As MSForms.ListBox
        Dim lCount As Long
       
        Set ws = ActiveSheet
        Set lb = ws.OLEObjects("SigsList").Object
       
        With lb
            For lCount = .ListCount - 1 To 0 Step -1
               .RemoveItem lCount
            Next
        End With

        Set ws = Nothing
        Set lb = Nothing
    End Sub

    Sub sigs_delete()
        Dim ws As Worksheet
        Dim lb As MSForms.ListBox
        Dim lCount As Long
       
        Set ws = ActiveSheet
        Set lb = ws.OLEObjects("SigsList").Object
       
        With lb
            For lCount = .ListCount - 1 To 0 Step -1
               If .Selected(lCount) Then .RemoveItem lCount
            Next
        End With

        Set ws = Nothing
        Set lb = Nothing
    End Sub

    Sub on_off_toggle()
        If is_on Then
            is_on = False
        Else
            is_on = True
        End If
    End Sub
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 

  ☞ 'sigs_clear'는 모든 신호 목록을 삭제합니다. 'sigs_delete'는 선택된 신호 목록을 삭제합니다. 'on_off_toggle'은 신호 출력을 On/Off 합니다.
 
  마지막으로 'CCandleFeeder'의 'fillCandle'과 'updateCandle'에서 호출하는 'sig_add'를 작성합니다.
  
  ※ (함수 'sig_add' 소스)
  
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 
    Function sig_add(s)
        Dim ws As Worksheet
        Dim lb As MSForms.ListBox
        Dim sarr As Variant
        If Not is_on Then Exit Function
       
        If sheetExist(SIGS_POSTFIX) Then
            Set ws = Sheets(SIGS_POSTFIX)
            Set lb = ws.OLEObjects("SigsList").Object
            sarr = Split(s, ".")
            lb.AddItem sarr(0)                      ''''time
            lb.List(lb.ListCount - 1, 1) = sarr(1)  ''''stock-candle
            lb.List(lb.ListCount - 1, 2) = sarr(2)  ''''indicator id
            lb.List(lb.ListCount - 1, 3) = sarr(3)  ''''signal
            Set ws = Nothing
            Set lb = Nothing
        End If
    End Function
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ☞  Multi-column 리스트 상자에 시간, 종목명-분봉, 지표 식별자 및 정수값 신호를 출력합니다.
   

▶ 'prepareSheet' 함수 수정  

  'prepareSheet' 함수에서 'createSigSheet' 함수를 호출하도록 수정합니다.

  
  ※ (수정 후 'prepareSheet' 함수 소스)
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Function prepareSheet(slist, ilist)
        Dim xlSheet As Worksheet
        Dim s_arr As Variant
        On Error GoTo EH_prepareSheet:
        dde_sheet = ActiveSheet.Name
       
        If ilist <> xlNullString Then Call createSigSheet       ''''added for 5-8
        Call prepareIndicators(ilist, True)       'for indicators
        s_arr = Split(slist, ",")
        s_cnt = UBound(s_arr) - LBound(s_arr) + 1
        ReDim ss_list(1 To s_cnt, 1 To 2)
        Set feeders = New Collection
       
        For i = LBound(s_arr) To UBound(s_arr)
            sn = s_arr(i)
            pospl = InStr(sn, "(")
            '''' for bug fix
            pospl = WorksheetFunction.Max(pospl, InStr(pospl + 1, sn, "("))
            pospr = InStr(sn, ")")
            '''' for bug fix
            pospr = WorksheetFunction.Max(pospr, InStr(pospr + 1, sn, ")"))
            cu = Right(sn, Len(sn) - pospr)
            s_code = Mid(sn, pospl + 1, pospr - pospl - 1)
            If s_code = Empty Or s_code = "" Then GoTo NextLoop:
           
            sh_name = Left(sn, pospl - 1)
            If sh_name = Empty Or sh_name = "" Then sh_name = s_code
            sh_name = sh_name & "-" & cu
           
            ss_list(i - LBound(s_arr) + 1, 1) = s_code
            ss_list(i - LBound(s_arr) + 1, 2) = sh_name
       
            Dim cfeeder As CCandleFeeder
            Set cfeeder = New CCandleFeeder
            cfeeder.StockCode = s_code
            cfeeder.CandleSheet = sh_name
            cfeeder.DDEsheet = dde_sheet
            cfeeder.CHTsheet = CHART_POSTFIX
            cfeeder.TIndicator = assignIndicators(s_code, sh_name)
            feeders.Add cfeeder, s_code
           
            If Not sheetExist(sh_name) Then
                Set xlSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
                xlSheet.Name = sh_name
               
                With xlSheet
                    .Cells(1, 1).Value = "일자 / 시간"
                    .Cells(1, 2).Value = "시가"
                    .Cells(1, 3).Value = "고가"
                    .Cells(1, 4).Value = "저가"
                    .Cells(1, 5).Value = "종가"
                    .Cells(1, 6).Value = "거래량"
                End With
            End If
    NextLoop:
        Next i
       
        Set xlSheet = Nothing
        Exit Function
       
    EH_prepareSheet:
        MsgBox "Error(" & Err.Number & ") " & Err.Description & " [prepareSheet]"
        Err.Raise Err.Number, "prepareSheet", Err.Description
    End Function


    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''  
  ☞ 하이라이트 된 한 줄만 추가해주면 니다.

  모든 작업이 끝났습니다. 이제 테스트를 해 볼까요?
 
▶ 테스트 결과  
 
그림 1. 신호출력 시트

  ※ 완성된 파일 소스