※ 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'를 진행하겠습니다.
댓글 없음:
댓글 쓰기