※ Elliott Pattern Helper Add In
'Excel에서 HTS DDE 활용하기 5' 시리즈의 마지막 글로서, 지표를 활용하여 신호를 포착하고 표시하는 방법에 관한 주제를 다루겠습니다.
기술적 지표는, 사용하는 사람들마다 나름대로의 목적이 있겠지만, 지정한 조건에 따라 신호를 발생시켜 매수 또는 매도의 시점을 찾는 것이 중요한 용도 중 하나일 것입니다.
Elliott oscillator MACD(5, 35, 5)를 예를 들어 볼까요?
- 5일 이동평균이 35일 이동평균을 상향돌파(golden cross)할 때와 하향돌파(dead cross)할 때를 각각 매수와 매도시점으로 정한다. 이는 각각 oscillator가 (-)에서 (+)로 변할 때, (+)에서 (-)에서 변할 때이다.
- Oscillator가 그 이동평균(signal line)을 상향돌파할 때와 하향돌파할 때를 각각 매수 및 매도시점으로 정한다.
- Bullish divergence: 주가는 신저가를 형성하는데 지표는 그렇지 않은 경우
- Bearish divergence: 주가는 신고가를 형성하지만 지표는 전고가보다 낮은 경우
증권사에서 제공하는 HTS는 대부분 조건검색 기능을 제공하여 사용자가 지정한 조건에 따라 신호를 발생시키도록 할 수 있습니다. 그러므로 굳이 프로그래밍 하지 않고 HTS를 사용할 수도 있겠지만, HTS가 제공하지 않는 지표나 신호 포착을 위한 방법이 필요할 것입니다.
지난 글에서 완성된 파일(☞)을 수정하여 몇몇 신호를 포착하고 정해진 시트에 출력하도록 하겠습니다.
▶ 지표 클래스 수정
먼저 현재까지 완성된 엑셀 VB 프로그램의 기본적인 작동 구조를 상기해 보겠습니다. DDE 연결 셀의 값이 변하면 'SetLinkOnData'로 지정된 함수를 통해 'CCandleFeeder' 오브젝트의 'store' 함수가 호출됩니다. 함수 'store'는 캔들정보를 축적함과 동시에 여러 지표 클래스의 허브(hub) 역할을 하는 'CStockIndicator' 클래스의 'updateLatest'를 호출하여 최종 지표값을 계산하도록 합니다.
MACD의 이동평균 cross나 oscillator와 signal의 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하도록 합니다. 아래 소스를 참고하십시요.
※ (참고) '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
''''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' 소스 ☞
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. 신호출력 시트 |
※ 완성된 파일 소스 ☞
댓글 없음:
댓글 쓰기