※ Elliott Pattern Helper Add In
지난 글에서 몇몇 기술적 지표의 구현을 위한 기초 작업을 완료했습니다. 그렇다면 지금까지 작성해왔던 VBA 프로그램과 통합해 보아야 겠지요.
우선 시나리오를 정리하면 두 개입니다. 하나는 지표를 축적하는 것이고, 다른 하나는 차트에 출력하는 것인데, 모두 DDE 이벤트를 반영해야 합니다. 즉, 축적되는 지표 자체나 이를 출력하는 차트 또한 DDE와 연동되어 update되어야 합니다.
- 'Schedule' 버튼을 클릭하면 축적할 지표를 선택할 수 있는 폼이 팝업된다. ※ 'FSchedule' 폼에 새로운 페이지를 추가하여 사용합니다.
- 지표를 선택하면 해당 지표의 기간(period)을 설정하는 텍스트 상자와 이동평균 사용시 단순이동평균 사용을 지시하는 체크박스가 활성화된다. ※ 지수이동평균이 default인데 이를 단순이동평균으로 변경할 경우에 사용합니다.
- 'OK' 버튼을 클릭하면 선택된 지표가 축적된다. ※ 'FSchedule' 폼을 통해 선택된 (종목×시간단위)의 캔들정보 시트에 축적됩니다.
- DDE 이벤트에 따라 지표가 refresh된다.
- 'Chart' 버튼을 클릭하면 세 가지 지표 중 하나를 선택하는 폼이 팝업된다.
- 기간을 설정하고 'OK' 버튼을 클릭하면 차트출력 시트("CHARTS")에 지표를 포함하는 캔들차트가 생성된다. ※ 기간은 '시나리오 1'에서 지정한 시간과 다를 수 있습니다.
- DDE 이벤트에 따라 차트가 refresh된다.
▶ (시나리오 1). 'FSchedule' 수정
시나리오 1을 위해 기존에 작성한 폼 'FSchedule'을 수정하겠습니다. 엑셀에서 메뉴 '개발 도구' → 'Visual Basic'을 선택하거나 키보드의 'Alt + F11'을 눌러 VBA 편집창을 띄웁니다.
VBAProject 트리 목록에서 'FSchedule'의 Context 메뉴 → '개체 보기'를 선택한 후 다중 페이지의 Context 메뉴 → '새 페이지'를 클릭하여 페이지를 생성합니다. 페이지의 Caption은 적절히 입력하십시요.
지표를 선택하기 위한 콤보상자를 생성합니다. 도구상자의 콤보상자를 선택하고(그림 1의 1), 새로 생성한 페이지에 위치시킨 후(그림 1의 2), 이름을 'ComboBoxTI'로 수정합니다(그림 1의 3).
![]() |
그림 1. 지표 선택을 위한 콤보상자 생성 |
![]() |
그림 2-1. 지표의 기간 설정을 위한 텍스트 상자 생성 1 |
다음 그림 2-2와 같이 동일한 방법으로 'TextBoxP2'와 'TextBoxP3'를 생성하고 레이블을 적절히 입력합니다.
![]() |
그림 2-2. 지표의 기간 설정을 위한 텍스트 상자 생성 2 |
지표 계산 시 지수이동평균이 아닌 단순이동평균을 사용하기 위한 체크박스를 생성합니다. 도구상자를 선택하고 체크박스를 선택한 후(그림 3의 1), 다음 그림과 같이 생성합니다(그림 3의 2). 이름은 'CheckBoxSMA'로 합니다(그림 3의 3). Caption은 제거하고 레이블을 적절히 입력합니다.
![]() |
그림 3. 단순이동평균 지정을 위한 체크박스 생성 |
지표를 추가하기 위한 버튼을 생성합니다. 도구상자의 명령단추를 선택하고(그림 4의 1), 아래 그림과 같이 생성합니다(그림 4의 2). 버튼 이름은 'ButtonAdd'로 수정합니다(그림 4의 3). Caption은 적절히 입력하십시요.
![]() |
그림 4. 지표를 추가하기 위한 버튼 생성 |
추가된 지표를 표시하기 위한 목록상자를 추가합니다. 도구상자에서 목록상자를 선택하고(그림 5의 1), 다음 그림과 같이 생성합니다(그림 5의 2). 목록상자 이름은 'ListBoxTI'로 합니다(그림 5의 3). 레이블은 적절히 추가하십시요.
![]() |
그림 5. 선택된 지표를 표시하는 목록상자 생성 |
위 그림 1에서 5까지 추가된 UI 콘트롤들의 이벤트 매크로를 작성하겠습니다.
먼저 선택 가능한 지표를 'ComboBoxTI'의 초기화 코드로 입력합니다. 더불어 콤보상자 'ComboBoxTI'를 통해 선택된 항목(지표)이 달라질 때마다 입력해야 할 기간(period)의 개수가 달라집니다. 이를 반영하기 위한 코드를 입력하겠습니다.
먼저 선택 가능한 지표를 'ComboBoxTI'의 초기화 코드로 입력합니다. 더불어 콤보상자 'ComboBoxTI'를 통해 선택된 항목(지표)이 달라질 때마다 입력해야 할 기간(period)의 개수가 달라집니다. 이를 반영하기 위한 코드를 입력하겠습니다.
※ (폼 'FSchedule'의 수정 후 'UserForm_Initialize' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub UserForm_Initialize()
ComboBoxTS.AddItem "09:00:00"
ComboBoxTS.AddItem "10:00:00"
ComboBoxTS.AddItem "11:00:00"
ComboBoxTS.AddItem "12:00:00"
ComboBoxTS.AddItem "13:00:00"
ComboBoxTS.AddItem "14:00:00"
ComboBoxTS.AddItem "15:00:00"
ComboBoxTS.Text = "09:00:00"
ComboBoxTE.AddItem "09:00:00"
ComboBoxTE.AddItem "10:00:00"
ComboBoxTE.AddItem "11:00:00"
ComboBoxTE.AddItem "12:00:00"
ComboBoxTE.AddItem "13:00:00"
ComboBoxTE.AddItem "14:00:00"
ComboBoxTE.AddItem "15:00:00"
ComboBoxTE.Text = "15:00:00"
ComboBoxCT.AddItem "5분", 0
ComboBoxCT.AddItem "15분", 1
ComboBoxCT.AddItem "60분", 2
ComboBoxCT.AddItem "일", 3
ComboBoxCT.ListIndex = 0
s_list = getStockList()
Set s_code = New Collection
For i = LBound(s_list, 1) To UBound(s_list, 1)
If s_list(i, 1) <> "" Then
s_code.Add s_list(i, 1), s_list(i, 2)
ComboBoxSN.AddItem s_list(i, 2), i - 1
End If
Next i
ComboBoxSN.ListIndex = 0
s_list = Split(sheets_cs(xlNullString), ",")
For i = LBound(s_list) To UBound(s_list)
ListBoxSC.AddItem Replace(s_list(i), "-", " ")
Next i
ComboBoxTI.AddItem "MACD", 0
ComboBoxTI.AddItem "RSI", 1
ComboBoxTI.AddItem "OBV", 2
ComboBoxTI.AddItem "FRTL", 3
ComboBoxTI.AddItem "PFTW", 4
Call DisableAllP
ListBoxTI.AddItem "MACD (지수 5 34 5)"
ListBoxTI.AddItem "RSI (지수 13)"
ListBoxTI.AddItem "OBV (지수 9)"
ListBoxTI.AddItem "FRTL (7)"
ListBoxTI.AddItem "PFTW"
End Sub
Private Sub DisableAllP()
TextBoxP1.Value = xlNullString
TextBoxP2.Value = xlNullString
TextBoxP3.Value = xlNullString
TextBoxP1.Enabled = False
TextBoxP2.Enabled = False
TextBoxP3.Enabled = False
End Sub
Private Sub EnableAllP()
TextBoxP1.Enabled = True
TextBoxP2.Enabled = True
TextBoxP3.Enabled = True
End Sub
Private Sub EnableP1()
TextBoxP2.Value = xlNullString
TextBoxP3.Value = xlNullString
TextBoxP1.Enabled = True
TextBoxP2.Enabled = False
TextBoxP3.Enabled = False
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub UserForm_Initialize()
ComboBoxTS.AddItem "09:00:00"
ComboBoxTS.AddItem "10:00:00"
ComboBoxTS.AddItem "11:00:00"
ComboBoxTS.AddItem "12:00:00"
ComboBoxTS.AddItem "13:00:00"
ComboBoxTS.AddItem "14:00:00"
ComboBoxTS.AddItem "15:00:00"
ComboBoxTS.Text = "09:00:00"
ComboBoxTE.AddItem "09:00:00"
ComboBoxTE.AddItem "10:00:00"
ComboBoxTE.AddItem "11:00:00"
ComboBoxTE.AddItem "12:00:00"
ComboBoxTE.AddItem "13:00:00"
ComboBoxTE.AddItem "14:00:00"
ComboBoxTE.AddItem "15:00:00"
ComboBoxTE.Text = "15:00:00"
ComboBoxCT.AddItem "5분", 0
ComboBoxCT.AddItem "15분", 1
ComboBoxCT.AddItem "60분", 2
ComboBoxCT.AddItem "일", 3
ComboBoxCT.ListIndex = 0
s_list = getStockList()
Set s_code = New Collection
For i = LBound(s_list, 1) To UBound(s_list, 1)
If s_list(i, 1) <> "" Then
s_code.Add s_list(i, 1), s_list(i, 2)
ComboBoxSN.AddItem s_list(i, 2), i - 1
End If
Next i
ComboBoxSN.ListIndex = 0
s_list = Split(sheets_cs(xlNullString), ",")
For i = LBound(s_list) To UBound(s_list)
ListBoxSC.AddItem Replace(s_list(i), "-", " ")
Next i
ComboBoxTI.AddItem "MACD", 0
ComboBoxTI.AddItem "RSI", 1
ComboBoxTI.AddItem "OBV", 2
ComboBoxTI.AddItem "FRTL", 3
ComboBoxTI.AddItem "PFTW", 4
Call DisableAllP
ListBoxTI.AddItem "MACD (지수 5 34 5)"
ListBoxTI.AddItem "RSI (지수 13)"
ListBoxTI.AddItem "OBV (지수 9)"
ListBoxTI.AddItem "FRTL (7)"
ListBoxTI.AddItem "PFTW"
End Sub
Private Sub DisableAllP()
TextBoxP1.Value = xlNullString
TextBoxP2.Value = xlNullString
TextBoxP3.Value = xlNullString
TextBoxP1.Enabled = False
TextBoxP2.Enabled = False
TextBoxP3.Enabled = False
End Sub
Private Sub EnableAllP()
TextBoxP1.Enabled = True
TextBoxP2.Enabled = True
TextBoxP3.Enabled = True
End Sub
Private Sub EnableP1()
TextBoxP2.Value = xlNullString
TextBoxP3.Value = xlNullString
TextBoxP1.Enabled = True
TextBoxP2.Enabled = False
TextBoxP3.Enabled = False
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 하이라이트 된 부분이 추가된 코드입니다.
콤보상자 'ComboBoxTI'에서 선택된 값에 따라 텍스트 상자 'TextBoxP1', 'TextBoxP2' 및 'TextBoxP3'의 초기화를 다르게 설정할 것입니다. 폼 'FSchedule'의 개체편집 모드에서 콤보상자 'ComboBoxTI'를 더블클릭하면 소스코드 편집 모드로 바뀌면서 'ComboBoxTI_Change' 매크로의 뼈대가 생성됩니다. 아래 소스를 참고하여 입력하십시요.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private Sub ComboBoxTI_Change()
Select Case ComboBoxTI.ListIndex
Case 0
Call EnableAllP
TextBoxP1.value = 5
TextBoxP2.value = 34
TextBoxP3.value = 5
Case 1
Call EnableP1
TextBoxP1.value = 13
Case 2
Call EnableP1
TextBoxP1.value = 9
Case 3
Call EnableP1
TextBoxP1.value = 7
Case 4
Call DisableAllP
End Select
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 선택된 지표에 따라 기간(period) 지정을 위한 텍스트 상자를 다르게 초기화합니다.
지표와 기간을 지정한 후 'Add' 버튼을 클릭하면 선택지표 목록상자 'ListBoxTI'에 목록이 추가되도록 해야 합니다. 폼 'FSchedule'의 개체편집 모드에서 'ButtonAdd'를 더블클릭하면 소스코드 편집 모드로 바뀌면서 'ButtonAdd_Click' 매크로의 뼈대가 생성됩니다. 아래 소스를 참고하여 입력하십시요.
※ ('ButtonAdd_Click' 이벤트 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Private Sub ButtonAdd_Click()
Dim ns As String
On Error GoTo Skip:
Select Case ComboBoxTI.ListIndex
Case 0
ns = "MACD ("
If TextBoxP1.value = xlNullString Then GoTo Skip:
If CInt(TextBoxP1.value) > 0 Then
ns = ns & TextBoxP1.value & " "
Else
GoTo Skip:
End If
If CInt(TextBoxP2.value) > 0 Then
ns = ns & TextBoxP2.value & " "
Else
GoTo Skip:
End If
If Not TextBoxP3.value = xlNullString Then
ns = ns & CInt(TextBoxP3.value) & ")"
Else
ns = Left(ns, Len(ns) - 1)
ns = ns & ")"
End If
Case 1
ns = "RSI ("
If CInt(TextBoxP1.value) > 0 Then
ns = ns & TextBoxP1.value & ")"
Else
GoTo Skip:
End If
Case 2
ns = "OBV"
If Not TextBoxP1.value = xlNullString Then
ns = ns & " (" & CInt(TextBoxP1.value) & ")"
Else
GoTo SkipMA:
End If
Case 3
ns = "FRTL ("
If CInt(TextBoxP1.value) > 0 Then
ns = ns & TextBoxP1.value & ")"
Else
GoTo Skip:
End If
Case 4
ns = "PFTW"
Case Else
GoTo Skip:
End Select
If ComboBoxTI.value <> "FRTL" Or ComboBoxTI.value <> "PFTW" Then
If CheckBoxSMA.value = True Then
ns = Replace(ns, "(", "(단순 ")
Else
ns = Replace(ns, "(", "(지수 ")
End If
End If
SkipMA:
For i = 0 To ListBoxTI.ListCount - 1
If ListBoxTI.List(i) = ns Then GoTo Skip:
Next i
ListBoxTI.AddItem ns
Skip:
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 'MACD (지수 5 34 5)', 'RSI (단순 14)'와 같은 형태의 문자열로 목록상자 'ListBoxTI'에 추가됩니다.
선택지표 목록상자 'ListBoxTI'에 추가된 목록 중의 특정 항목을 더블클릭하면 해당 항목의 선택이 취소되면서 'ListBoxTI'에서 사라지도록 할 것입니다. 아래 'ListBoxTI_DblClick' 소스를 'ComboBoxTI_Change' 코드 밑에 입력하십시요.
선택지표 목록상자 'ListBoxTI'에 추가된 목록 중의 특정 항목을 더블클릭하면 해당 항목의 선택이 취소되면서 'ListBoxTI'에서 사라지도록 할 것입니다. 아래 'ListBoxTI_DblClick' 소스를 'ComboBoxTI_Change' 코드 밑에 입력하십시요.
※ ('ListBoxTI_DblClick' 이벤트 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Private Sub ListBoxTI_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
If Not ListBoxTI.ListIndex < 0 Then _
ListBoxTI.RemoveItem ListBoxTI.ListIndex
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
폼 'FSchedule'의 'OK' 버튼 이벤트 매크로를 수정해야 합니다. 기존 소스는 캔들정보를 축적할 종목 리스트와 스케줄 시각을 전달하면서 'onSchedule'을 호출합니다. 선택된 지표 목록을 함께 전달하도록 수정합니다.
※ (수정 후 'ButtonOK_Click' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''Private Sub ButtonOK_Click()
Dim s_list As String 'comma separated selected stock list
Dim i_list As String 'comma separated selected indicator list
Me.Hide
For i = 0 To ListBoxSC.ListCount - 1
sitem = ListBoxSC.List(i)
sn = Split(sitem, " ")
sc = s_code.Item(sn(LBound(sn)))
sitem = Replace(sitem, " ", "(" & sc & ")")
s_list = s_list & sitem & ","
Next i
For i = 0 To ListBoxTI.ListCount - 1
i_list = i_list & ListBoxTI.List(i) & ","
Next i
If Len(s_list) > 0 Then
s_list = Left(s_list, Len(s_list) - 1)
If Len(i_list) > 0 Then i_list = Left(i_list, Len(i_list) - 1)
Call onSchedule(ComboBoxTS.Text, ComboBoxTE.Text, _
s_list, i_list)
End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 하이라이트 된 부분이 수정된 곳입니다.
☞ 'i_list' 변수가 'MACD (지수 5 34 5),RSI (단순 14)'와 같이 comma-separated string 형태로 선택지표 목록을 저장하여 'onSchedule'에 전달됩니다.
☞ 'i_list' 변수가 'MACD (지수 5 34 5),RSI (단순 14)'와 같이 comma-separated string 형태로 선택지표 목록을 저장하여 'onSchedule'에 전달됩니다.
'FSchedule' 폼의 수정은 완료되었습니다.
※ 'FSchedule' 폼 소스 ☞
▶ (시나리오 1). Module 수정
폼 'FSchedule'의 'OK' 버튼 클릭 시 호출되는 'onSchedule'을 수정합니다.
※ (수정 후 'onSchedule' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub onSchedule(ts, te, sc, il)
Call registerLinks
Call prepareSheet(sc, il)
If Time > TimeValue(ts) And Time < TimeValue(te) Then
Application.OnTime Now, "Start_Click"
Else
Application.OnTime TimeValue(ts), "Start_Click"
End If
Application.OnTime TimeValue(te), "Stop_Click"
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub onSchedule(ts, te, sc, il)
Call registerLinks
Call prepareSheet(sc, il)
If Time > TimeValue(ts) And Time < TimeValue(te) Then
Application.OnTime Now, "Start_Click"
Else
Application.OnTime TimeValue(ts), "Start_Click"
End If
Application.OnTime TimeValue(te), "Stop_Click"
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 선택지표 목록(comma-separated string, 'il')을 저장하는 인자가 추가되었습니다.
마찬가지로 'onSchedule'이 'prepareSheet'을 호출할 때 선택지표 목록을 추가로 넘겨주도록 수정합니다. 함수 'prepareIndicators'를 신규로 작성하고, 'prepareSheet'는 넘겨받은 지표 목록을 인자로 넘기면서 'prepareIndicators'를 호출하도록 하겠습니다.
※ (수정 후 'prepareSheet' 소스)
마찬가지로 'onSchedule'이 'prepareSheet'을 호출할 때 선택지표 목록을 추가로 넘겨주도록 수정합니다. 함수 'prepareIndicators'를 신규로 작성하고, 'prepareSheet'는 넘겨받은 지표 목록을 인자로 넘기면서 'prepareIndicators'를 호출하도록 하겠습니다.
※ (수정 후 'prepareSheet' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function prepareSheet(slist, ilist)
Dim xlSheet As Worksheet
Dim s_arr As Variant
On Error GoTo EH_prepareSheet:
dde_sheet = ActiveSheet.Name
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, "(")
pospr = InStr(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
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function prepareSheet(slist, ilist)
Dim xlSheet As Worksheet
Dim s_arr As Variant
On Error GoTo EH_prepareSheet:
dde_sheet = ActiveSheet.Name
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, "(")
pospr = InStr(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
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 인자(선택지표 목록, 'ilist')가 추가되었습니다.
☞ 'prepareIndicators' 함수를 호출하는 코드가 추가되었습니다.
☞ 지난 글에서 'CCandleFeeder' 클래스에 멤버 변수(property) 'pTIndicator'를 추가했습니다. 이를 초기화하기 위해 함수 'assignIndicators'를 호출합니다.
신규 함수 'prepareIndicators'와 'assignIndicators'를 작성하겠습니다.
'prepareIndicators'는 폼 'FSchedule'을 통해 입력받은 지표목록을 구조체 'indicator_t'의 배열로 저장함으로써 다른 함수에서 참조할 수 있도록합니다. 아래 소스를 복사하여 입력하십시요.
※ (함수 'prepareIndicators' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function prepareIndicators(ilist, b)
Dim indi_t As indicator_t
On Error GoTo EH_prepareIndicators:
If b Then
si_list = Split(ilist, ",")
If UBound(si_list) < 0 Then Exit Function
ReDim it_arr(LBound(si_list) To UBound(si_list))
For i = LBound(si_list) To UBound(si_list)
indi_t = indicator_type(si_list(i))
If indi_t.i_type <> 0 Then it_arr(i) = indi_t
NextLoop:
Next i
Else
On Error Resume Next
ub = UBound(si_list)
If Err.Number = 13 Then si_list = Split(ilist, ",")
On Error GoTo 0
For i = LBound(si_list) To UBound(si_list)
If ilist = si_list(i) Then Exit Function
Next i
indi_t = indicator_type(ilist)
If indi_t.i_type <> 0 Then
ReDim Preserve si_list(LBound(si_list) To UBound(si_list) + 1)
si_list(UBound(si_list)) = ilist
ReDim Preserve it_arr(LBound(si_list) To UBound(si_list))
it_arr(UBound(it_arr)) = indi_t
For i = 1 To UBound(ss_list)
Dim fdr As CCandleFeeder
On Error Resume Next
Set fdr = feeders.Item(ss_list(i, 1))
If Err.Number = 5 Then GoTo Continue:
On Error GoTo 0
If Not fdr Is Nothing Then _
fdr.TIndicator = assignIndicators(ss_list(i, 1), ss_list(i, 2))
Continue:
Next i
End If
End If
Exit Function
EH_prepareIndicators:
MsgBox "Error(" & Err.Number & ") " & Err.Description & " [prepareIndicators]"
Err.Raise Err.Number, "prepareIndicators", Err.Description
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 지표목록(comma-separated string)을 구조체('indicator_t') 배열로 저장합니다.
☞ 'it_arr'이 'indicator_t' 구조체 배열입니다. 전역변수로 선언해야 합니다. 'prepareSheet' 함수가 있는 모듈의 선언부에 'Private it_arr() As indicator_t' 라인을 추가하십시요.
☞ 'si_list'는 지표목록을 string 배열로 저장하기 위한 전역변수입니다. 'prepareSheet' 함수가 있는 모듈의 선언부에 'Private si_list As Variant' 라인을 추가하십시요.
☞ 지표 string(예: 'MACD (지수 5 34 6)')을 'indicator_t'로 변환하기 위해 함수 'indicator_type'을 호출합니다. 아래 소스를 참고하십시요.
※ (함수 'indicator_type' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function indicator_type(istr) As indicator_t
Dim indi_t As indicator_t, t_arr As Variant, p_arr() As Integer
t_arr = Split(istr, " ")
lB = LBound(t_arr)
ub = UBound(t_arr)
If ub < 0 Then Exit Function
Dim mtp As ma_t
mtp = 0
If InStr(istr, "지수") > 1 Then
mtp = ma_e 'default MA type = EMA
ElseIf InStr(istr, "단순") > 1 Then
mtp = ma_s
End If
Select Case t_arr(lB)
Case "MACD"
indi_t.i_type = 2 ^ 1
ReDim p_arr(1 To ub - lB - 1)
For j = lB + 2 To ub
p_arr(j - lB - 1) = CInt(Replace(t_arr(j), ")", ""))
Next j
indi_t.peri = p_arr
If ub - lB - 1 > 2 Then indi_t.i_type = indi_t.i_type + 1
Case "RSI"
indi_t.i_type = 2 ^ 2
ReDim p_arr(1 To 1)
p_arr(1) = CInt(Replace(t_arr(ub), ")", ""))
indi_t.peri = p_arr
Case "OBV"
indi_t.i_type = 2 ^ 3
If mtp > 0 Then
ReDim p_arr(1 To 1)
p_arr(1) = CInt(Replace(t_arr(ub), ")", ""))
indi_t.peri = p_arr
indi_t.i_type = indi_t.i_type + 1
End If
Case "FRTL"
indi_t.i_type = 2 ^ 4
ReDim p_arr(1 To 1)
p_arr(1) = CInt(Replace(Replace(t_arr(ub), ")", ""), "(", ""))
indi_t.peri = p_arr
Case "PFTW"
indi_t.i_type = 2 ^ 5
Case Else
Exit Function
End Select
indi_t.m_type = mtp
indicator_type = indi_t
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'assignIndicators' 함수는 주어진 (종목×캔들정보 시트)에 대응하는 'CCandleFeeder' 클래스의 'pTIndicator' 멤버('CStockIndicator' 배열)를 초기화하기 위한 것입니다. 아래 소스를 참고하십시요.
※ (함수 'assignIndicators' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function assignIndicators(s_code, c_sheet) As Variant
Dim indi_arr() As CStockIndicator
On Error GoTo EH_assignIndicators:
If UBound(it_arr) < 0 Then Exit Function
ReDim indi_arr(LBound(it_arr) To UBound(it_arr))
icol = 7 'indicator column
For i = LBound(it_arr) To UBound(it_arr)
Dim sindicator As CStockIndicator, tArr As Variant
Set sindicator = New CStockIndicator
sindicator.CandleSheet = c_sheet
sindicator.Indicator = it_arr(i).i_type
sindicator.MA = it_arr(i).m_type
sindicator.Peroids = it_arr(i).peri
sindicator.Col = icol 'sindicator.init()
icol = sindicator.init()
Set indi_arr(i) = sindicator
NextLoop:
Next i
MyExit:
assignIndicators = indi_arr
Debug.Print "assignIndicator total" & UBound(indi_arr) - LBound(indi_arr) + 1
Exit Function
EH_assignIndicators:
If Err.Number = 8911 Then
Debug.Print "Error(8911) " & Err.Source
Err.Clear
GoTo NextLoop:
End If
Debug.Print "Error(" & Err.Number & ") " & Err.Description & " [assignIndicators]"
GoTo MyExit:
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 'CStockIndicator' 오브젝트를 생성하면서 'init'을 호출하여 지표 초기계산을 완료합니다.
시나리오 1의 구현이 완료되었습니다. 파일을 저장한 후 테스트해 보겠습니다.
'Schedule' 버튼을 클릭하여 'FSchedule' 폼을 팝업시키고 위의 그림 6과 같이 지정하여 'OK' 버튼을 클릭하니 그림 7의 결과를 얻을 수 있었습니다.
※ 현재까지 완성된 엑셀 파일입니다. ☞
※ 다음 글에서 '시나리오 2'를 진행하겠습니다.
☞ 'prepareIndicators' 함수를 호출하는 코드가 추가되었습니다.
☞ 지난 글에서 'CCandleFeeder' 클래스에 멤버 변수(property) 'pTIndicator'를 추가했습니다. 이를 초기화하기 위해 함수 'assignIndicators'를 호출합니다.
신규 함수 'prepareIndicators'와 'assignIndicators'를 작성하겠습니다.
'prepareIndicators'는 폼 'FSchedule'을 통해 입력받은 지표목록을 구조체 'indicator_t'의 배열로 저장함으로써 다른 함수에서 참조할 수 있도록합니다. 아래 소스를 복사하여 입력하십시요.
※ (함수 'prepareIndicators' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function prepareIndicators(ilist, b)
Dim indi_t As indicator_t
On Error GoTo EH_prepareIndicators:
If b Then
si_list = Split(ilist, ",")
If UBound(si_list) < 0 Then Exit Function
ReDim it_arr(LBound(si_list) To UBound(si_list))
For i = LBound(si_list) To UBound(si_list)
indi_t = indicator_type(si_list(i))
If indi_t.i_type <> 0 Then it_arr(i) = indi_t
NextLoop:
Next i
Else
On Error Resume Next
ub = UBound(si_list)
If Err.Number = 13 Then si_list = Split(ilist, ",")
On Error GoTo 0
For i = LBound(si_list) To UBound(si_list)
If ilist = si_list(i) Then Exit Function
Next i
indi_t = indicator_type(ilist)
If indi_t.i_type <> 0 Then
ReDim Preserve si_list(LBound(si_list) To UBound(si_list) + 1)
si_list(UBound(si_list)) = ilist
ReDim Preserve it_arr(LBound(si_list) To UBound(si_list))
it_arr(UBound(it_arr)) = indi_t
For i = 1 To UBound(ss_list)
Dim fdr As CCandleFeeder
On Error Resume Next
Set fdr = feeders.Item(ss_list(i, 1))
If Err.Number = 5 Then GoTo Continue:
On Error GoTo 0
If Not fdr Is Nothing Then _
fdr.TIndicator = assignIndicators(ss_list(i, 1), ss_list(i, 2))
Continue:
Next i
End If
End If
Exit Function
EH_prepareIndicators:
MsgBox "Error(" & Err.Number & ") " & Err.Description & " [prepareIndicators]"
Err.Raise Err.Number, "prepareIndicators", Err.Description
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 지표목록(comma-separated string)을 구조체('indicator_t') 배열로 저장합니다.
☞ 'it_arr'이 'indicator_t' 구조체 배열입니다. 전역변수로 선언해야 합니다. 'prepareSheet' 함수가 있는 모듈의 선언부에 'Private it_arr() As indicator_t' 라인을 추가하십시요.
☞ 'si_list'는 지표목록을 string 배열로 저장하기 위한 전역변수입니다. 'prepareSheet' 함수가 있는 모듈의 선언부에 'Private si_list As Variant' 라인을 추가하십시요.
☞ 지표 string(예: 'MACD (지수 5 34 6)')을 'indicator_t'로 변환하기 위해 함수 'indicator_type'을 호출합니다. 아래 소스를 참고하십시요.
※ (함수 'indicator_type' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function indicator_type(istr) As indicator_t
Dim indi_t As indicator_t, t_arr As Variant, p_arr() As Integer
t_arr = Split(istr, " ")
lB = LBound(t_arr)
ub = UBound(t_arr)
If ub < 0 Then Exit Function
Dim mtp As ma_t
mtp = 0
If InStr(istr, "지수") > 1 Then
mtp = ma_e 'default MA type = EMA
ElseIf InStr(istr, "단순") > 1 Then
mtp = ma_s
End If
Select Case t_arr(lB)
Case "MACD"
indi_t.i_type = 2 ^ 1
ReDim p_arr(1 To ub - lB - 1)
For j = lB + 2 To ub
p_arr(j - lB - 1) = CInt(Replace(t_arr(j), ")", ""))
Next j
indi_t.peri = p_arr
If ub - lB - 1 > 2 Then indi_t.i_type = indi_t.i_type + 1
Case "RSI"
indi_t.i_type = 2 ^ 2
ReDim p_arr(1 To 1)
p_arr(1) = CInt(Replace(t_arr(ub), ")", ""))
indi_t.peri = p_arr
Case "OBV"
indi_t.i_type = 2 ^ 3
If mtp > 0 Then
ReDim p_arr(1 To 1)
p_arr(1) = CInt(Replace(t_arr(ub), ")", ""))
indi_t.peri = p_arr
indi_t.i_type = indi_t.i_type + 1
End If
Case "FRTL"
indi_t.i_type = 2 ^ 4
ReDim p_arr(1 To 1)
p_arr(1) = CInt(Replace(Replace(t_arr(ub), ")", ""), "(", ""))
indi_t.peri = p_arr
Case "PFTW"
indi_t.i_type = 2 ^ 5
Case Else
Exit Function
End Select
indi_t.m_type = mtp
indicator_type = indi_t
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'assignIndicators' 함수는 주어진 (종목×캔들정보 시트)에 대응하는 'CCandleFeeder' 클래스의 'pTIndicator' 멤버('CStockIndicator' 배열)를 초기화하기 위한 것입니다. 아래 소스를 참고하십시요.
※ (함수 'assignIndicators' 소스)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function assignIndicators(s_code, c_sheet) As Variant
Dim indi_arr() As CStockIndicator
On Error GoTo EH_assignIndicators:
If UBound(it_arr) < 0 Then Exit Function
ReDim indi_arr(LBound(it_arr) To UBound(it_arr))
icol = 7 'indicator column
For i = LBound(it_arr) To UBound(it_arr)
Dim sindicator As CStockIndicator, tArr As Variant
Set sindicator = New CStockIndicator
sindicator.CandleSheet = c_sheet
sindicator.Indicator = it_arr(i).i_type
sindicator.MA = it_arr(i).m_type
sindicator.Peroids = it_arr(i).peri
sindicator.Col = icol 'sindicator.init()
icol = sindicator.init()
Set indi_arr(i) = sindicator
NextLoop:
Next i
MyExit:
assignIndicators = indi_arr
Debug.Print "assignIndicator total" & UBound(indi_arr) - LBound(indi_arr) + 1
Exit Function
EH_assignIndicators:
If Err.Number = 8911 Then
Debug.Print "Error(8911) " & Err.Source
Err.Clear
GoTo NextLoop:
End If
Debug.Print "Error(" & Err.Number & ") " & Err.Description & " [assignIndicators]"
GoTo MyExit:
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
☞ 'CStockIndicator' 오브젝트를 생성하면서 'init'을 호출하여 지표 초기계산을 완료합니다.
시나리오 1의 구현이 완료되었습니다. 파일을 저장한 후 테스트해 보겠습니다.
![]() |
그림 6. 테스트 |
'Schedule' 버튼을 클릭하여 'FSchedule' 폼을 팝업시키고 위의 그림 6과 같이 지정하여 'OK' 버튼을 클릭하니 그림 7의 결과를 얻을 수 있었습니다.
![]() |
그림 7. 테스트 결과 |
※ 현재까지 완성된 엑셀 파일입니다. ☞
※ 다음 글에서 '시나리오 2'를 진행하겠습니다.
댓글 없음:
댓글 쓰기