終於到最後一節了,這裡完整測試了登入、報價、記錄、出現訊號、送至下單匣並收取回報資料。
我把程式碼貼在影片下方,請先從頭依課程影片試著做做看,然後再來比對出入的地方。

程式碼


工作表1

Dim WithEvents skC As SKCOMLib.SKCenterLib
Dim WithEvents skQ As SKCOMLib.SKQuoteLib
Public strID As String, strPW As String
Private Sub btnQuote_Click()
'開始報價,清除報價、記錄、已送註記、下單匣
ThisWorkbook.Names("報價").RefersToRange.ClearContents
Names("訊號1").RefersToRange.Offset(1, 1).Resize(1048575, 1).ClearContents
ThisWorkbook.Names("記錄").RefersToRange.ClearContents
工作表2.[2:1048576].ClearContents
Dim strStock As String
Dim nPage As Integer, rt As Integer
nPage = 1
strStock = [a2]
For Each Rng In [a3:a91]
strStock = strStock & "," & Rng
Next
rt = skQ.SKQuoteLib_RequestStocks(nPage, strStock)
End Sub
Private Sub btnRecord_Click()
If Time <= #1:30:00 PM# Then
Names("記錄欄位").RefersToRange = WorksheetFunction.Match(CSng(Time) + 1 / 8640000, ThisWorkbook.Names("時間起點").RefersToRange.Resize(1, 5402), 1)
Application.OnTime Date + ThisWorkbook.Names("時間起點").RefersToRange.Offset(0, Names("記錄欄位").RefersToRange), "工作表1.Record"
End If
End Sub
Private Sub Record()
Dim intRows As Integer
Dim intC As Integer, intN As Integer
Dim rngStart As Range
Set rngStart = ThisWorkbook.Names("時間起點").RefersToRange
intC = Names("記錄欄位").RefersToRange.Value
intN = ThisWorkbook.Names("欄數").RefersToRange.Value
intRows = [a1048576].End(xlUp).Row - 1
rngStart.Offset(1, intC).Resize(intRows, 1).Value = [c2].Resize(intRows, 1).Value
'填前價與高低
If intC > intN Then
[n2].Resize(intRows, 1).Value = rngStart.Offset(1, intC - intN).Resize(intRows, 1).Value
For Each Rng In [o2].Resize(intRows, 1)
Rng.Value = WorksheetFunction.Max(rngStart.Offset(Rng.Row - 1, intC - intN).Resize(1, intN))
Rng.Offset(0, 1).Value = WorksheetFunction.Min(rngStart.Offset(Rng.Row - 1, intC - intN).Resize(1, intN))
Next
End If
'設定下次記錄時間
If rngStart.Offset(0, intC + 1) < TimeValue("13:30:00") Then
Application.OnTime rngStart.Offset(0, intC + 1), "工作表1.Record"
Names("記錄欄位").RefersToRange = intC + 1
End If
End Sub
Private Sub btnStopRecord_Click()
On Error Resume Next
Application.OnTime ThisWorkbook.Names("時間起點").RefersToRange.Offset(0, Names("記錄欄位").RefersToRange.Value), "工作表1.Record", , False
End Sub
Private Sub CommandButton1_Click()
If skC Is Nothing Then
Set skC = New SKCOMLib.SKCenterLib
Set skQ = New SKCOMLib.SKQuoteLib
Set 工作表3.skR = New SKCOMLib.SKReplyLib
Set 工作表2.skO = New SKCOMLib.SKOrderLib
End If
UserForm1.Show
Dim rt As Integer
rt = skC.SKCenterLib_Login(strID, strPW)
If rt = 0 Then
Call 工作表3.connect_reply
rt = 工作表2.Ini_Order()
rt = skQ.SKQuoteLib_EnterMonitor
Else
tbxR.Text = "下單失敗, rt=" & rt
End If
End Sub
Private Sub skQ_OnConnection(ByVal nKind As Long, ByVal nCode As Long)
Call addText(CStr(nKind))
End Sub
'新增通知訊息
Sub addText(strMsg As String)
tbxR.Text = strMsg & vbCrLf & tbxR.Text
End Sub
Private Sub skQ_OnNotifyQuote(ByVal sMarketNo As Integer, ByVal sStockIdx As Integer)
If Application.Ready Then
Dim st As SKCOMLib.SKSTOCK
Dim rt As Integer
rt = skQ.SKQuoteLib_GetStockByIndex(sMarketNo, sStockIdx, st)
Dim intRow As Integer
intRow = WorksheetFunction.Match(st.bstrStockNo, [a2:a91], 0)
Dim a(11)
a(0) = st.bstrStockName
a(1) = st.nClose / 10 ^ st.sDecimal
a(2) = st.nTQty
a(3) = st.nUp / 10 ^ st.sDecimal
a(4) = st.nDown / 10 ^ st.sDecimal
a(5) = st.nBid / 10 ^ st.sDecimal
a(6) = st.nBc
a(7) = st.nAsk / 10 ^ st.sDecimal
a(8) = st.nAc
a(9) = st.nHigh / 10 ^ st.sDecimal
a(10) = st.nLow / 10 ^ st.sDecimal
a(11) = st.nOpen / 10 ^ st.sDecimal
[a1].Offset(intRow, 1).Resize(1, 12) = a
'檢查訊號
Call checkSignal([a1].Offset(intRow - 1, 0))
End If
End Sub
'檢查訊號
Private Sub checkSignal(rngCode As Range)
If Names("訊號1").RefersToRange.Offset(rngCode.Row - 1) = "sell" Then
If Names("訊號1").RefersToRange.Offset(rngCode.Row - 1, 1) = "" Then
With 工作表2.[c1048576].End(xlUp).Offset(1, 0)
.Value = rngCode
.Offset(0, 1) = rngCode.Offset(0, 1)
.Offset(0, 2) = "S"
.Offset(0, 3) = rngCode.Offset(0, 4)    '漲停賣出
.Offset(0, 4) = 1
.Offset(0, 16).Formula = "=" & rngCode.Offset(0, 2).Address(external:=True)
If cbxToOrder.Value Then
Call 工作表2.real_Order(.Cells(1, 1))
End If
End With
Names("訊號1").RefersToRange.Offset(rngCode.Row - 1, 1) = "訊號1"
Call 工作表3.RedoReply
End If
End If
End Sub
Private Sub skR_OnReplyMessage(ByVal bstrUserID As String, ByVal bstrMessage As String, sConfirmCode As Integer)
sConfirmCode = True
'MsgBox (bstrMessage)
End Sub

工作表2

Public WithEvents skO As SKCOMLib.SKOrderLib
Function Ini_Order() As Integer
Dim rt As Integer
rt = skO.SKOrderLib_Initialize
If rt = 0 Then
rt = skO.ReadCertByID(工作表1.strID)
End If
Ini_Order = rt
End Function
Sub test_Order()
Dim st As STOCKORDER
st = stock_order("6104", "65.5", 0, 0, 1)
Dim rt As Integer
Dim strMsg As String
rt = skO.SendStockOrder(工作表1.strID, False, st, strMsg)
Dim rng2 As Range, rng3 As Range
Set rng2 = [a1048576].End(xlUp).Offset(1, 0)
rng2 = strMsg
Set rng3 = 工作表3.[a1048576].End(xlUp).Offset(1, 0)
rng3 = strMsg
rng2.Offset(0, 5).Resize(1, 5).FormulaArray = "=" & rng3.Offset(0, 6).Resize(1, 5).Address(external:=True)
Call 工作表3.RedoReply
End Sub
Sub real_Order(rngCode As Range)
If Not cbxStopAll.Value Then
If CInt(tbxOrderLimit.Text) > 0 Then
'送單,限制減一
tbxOrderLimit.Text = CInt(tbxOrderLimit.Text) - 1
Dim st As STOCKORDER
Dim intBS As Integer, intFlag As Integer
If rngCode.Offset(0, 2) = "B" Then
intBS = 0
intFlag = 0
ElseIf rngCode.Offset(0, 2) = "S" Then
intBS = 1
intFlag = 3
End If
st = stock_order(rngCode.Value, rngCode.Offset(0, 3), intBS, intFlag, rngCode.Offset(0, 4))
Dim rt As Integer
Dim strMsg As String
rt = skO.SendStockOrder(工作表1.strID, False, st, strMsg)
rngCode.Offset(0, -2) = strMsg
Dim rng3 As Range
Set rng3 = 工作表3.[a1048576].End(xlUp).Offset(1, 0)
rng3.Value = strMsg
rngCode.Offset(0, 3).Resize(1, 5).FormulaArray = "=" & rng3.Offset(0, 6).Resize(1, 5).Address(external:=True)
End If
Call 工作表3.RedoReply
End If
End Sub
'sFlag;          //0:現股 1:融資 2:融券 3:無券
Private Function stock_order(strCode As String, strPrice As String, sbuysell As Integer, sflag As Integer, intQty As Integer) As SKCOMLib.STOCKORDER
Dim st As SKCOMLib.STOCKORDER
st.bstrFullAccount = [xfd1]
st.bstrPrice = strPrice
st.bstrStockNo = strCode
st.nQty = intQty
st.nSpecialTradeType = IIf(strPrice = "0", 1, 2)
st.nTradeType = 0
st.sbuysell = sbuysell
st.sflag = sflag
st.sPeriod = 0
st.sPrime = 0
stock_order = st
End Function

工作表3

Public WithEvents skR As SKCOMLib.SKReplyLib
Dim strData As String
Enum ri
KeyNo       '委託序號(成交回報無此欄)
MarketType  ' TS:證券 TA:盤後 TL:零股 TF:期貨
'TO:選擇權 OF:海期 OO:海選 OS:複委託
sType       ' N:委託 C:取消 U:改量 P:改價 (改價含證券逐筆)
' D:成交 B:改價改量
' S:動態退單
OrderErr    ' Y:失敗 T:逾時 N:正常
Broker      ' TS,TA,TL: 分公司代號 unit no , TF,TO: IB 代號 broker id
CustNo      ' 交易帳號
BuySell
' 證[0] B/S 買/賣,[1,2] 00 現股,01 代資,02 代券,03 融資,04 融券,20 零股,40 拍賣現股
' 證逐筆[0] B/S 買/賣,[1,2] 00 現股,01 代資,02 代券,03 融資,04 融券,20 零股,40 拍賣現股 [3] I/R/F  IOC / ROD / FOK [4] 1/2 市價/限價
' 期[0] B/S 買/賣,[1] Y/當沖, N/新倉, O/平倉, 7/代沖銷, [2] I/R/F  IOC / ROD / FOK,[3] 1/2/3/4/5 市價/限價/停損/停損限價/收市
' 權[0] B/S 買/賣,[1] N/O 新倉 / 平倉, 7/代沖銷,[2] I/R/F IOC / ROD / FOK,[3]1/2/3/4/5 市價/限價/停損/停損限價/收市
'海期海選[0] B/S 買/賣,[1]1/2/3/4/5 市價/限價/停損/停損限價/收市,[2] Y/N/O 當沖 / 新倉 / 平倉
'複委託[0] B/S 買/賣,[1]1/2/3/4/5 市價/限價/停損/停損限價/收市
ExchangeID  ' 交易所
ComId       ' 商品代碼
StrikePrice ' 履約價 七位整數
OrderNo     '  委託書號
Price       ' 價格, 已經處理的價格
' 其餘為根據 Type 種類不同,「委託」為委託價;「成交」為成交價;
' 「改價」為修改後價格;「動態退單」為交易所提供之退單基準價
Numerator   ' 海外期貨回報用,分子
Denominator ' 海外期貨回報用,分母
Price1      '海外期貨回報用,觸發價格
'國內期選成交時,第一隻腳成交價
Numerator1  '海外期貨回報用,觸發價格分子
Denominator1 ' 海外期貨回報用,觸發價格分母
Price2      '國內期選成交時,第二隻腳成交價
Numerator2 '
Denominator2 '
Qty         ' TS OS股數/ TF TO OF OO口數
' 根據 Type 種類,「委託」為委託量,「成交」為成交量,「改量」為減量數,「刪單」為原委託剩量
BeforeQty   ' 參考欄位,異動變更前量,刪單為空值
AfterQty    ' 參考欄位,異動變更後量,刪單為空值
Date        ' 交易日期
tTime        ' 交易時間(含冒號EX: 01:02:03)
OkSeq       ' 成交序號
SubID       ' 子帳帳號
SaleNo      ' 營業員編號
Agent       ' 委託介面
TradeDate   '委託日期(僅提供海外委託,國內尚未提供)
MsgNo       '回報流水號
PreOrder    ' A:盤中單 B:預約單(僅國內期、選委託)
ComId1      '第一隻腳商品代碼
YearMonth1  '第一隻腳商品結算年月
StrikePrice1 ' 第一隻腳商品履約價
ComId2      '第二隻腳商品代碼
YearMonth2  '第二隻腳商品結算年月
StrikePrice2 ' 第二隻腳商品履約價
ExecutionNo '成交序號
PriceSymbol '下單期標
Reserved    '盤別'A:T盤  B:T+1盤 (僅國內期、選委託)
OrderEffective '有效委託日
CallPut     '選擇權類型C:Call P:Put
OrderSeq    '交易所單號(依海外交易所實際提供為主)
End Enum
Private Sub skR_OnComplete(ByVal bstrUserID As String)
Call 工作表1.addText("complete")
End Sub
Private Sub skR_OnNewData(ByVal bstrUserID As String, ByVal bstrData As String)
If Application.Ready Then
Dim strI() As String
strI = Split(bstrData, ",")
'只收本帳號的回報
If strI(ri.Broker) & strI(ri.CustNo) = 工作表2.[xfd1] Then
'正常才繼續
If strI(ri.OrderErr) = "N" Then
If WorksheetFunction.CountIf(Range([a2], [a1048576].End(xlUp)), strI(ri.KeyNo)) = 0 Then
Call NewRow(strI)
Else
Call AlterRow(strI)
End If
Else
Call 工作表1.addText("Error: " & strI(ri.ComId))
End If
End If
Else
strData = bstrData & "\" & strData
End If
End Sub
'重讀報價
Sub RedoReply()
Dim strI() As String
strI = Split(strData, "\")
For Each stra In strI
If Len(CStr(stra)) > 10 Then
Call skR_OnNewData("", stra)
End If
Next
strData = ""
End Sub
'新增回報
Private Sub NewRow(strI)
With [a1048576].End(xlUp).Offset(1, 0)
.Value = strI(ri.KeyNo)
.Offset(0, 1) = strI(ri.OrderNo)
.Offset(0, 2) = strI(ri.ComId)
.Offset(0, 3) = Code2Name(strI(ri.ComId))
.Offset(0, 4) = Mid(strI(ri.BuySell), 1, 1)
.Offset(0, 11) = CDate(strI(ri.tTime))
Select Case strI(ri.sType)
Case "N"
.Offset(0, 5) = CDate(strI(ri.tTime))
.Offset(0, 6) = CSng(strI(ri.Price))
.Offset(0, 7) = CInt(strI(ri.Qty)) / 1000
Case "C", "U"
.Offset(0, 8) = CInt(strI(ri.Qty)) / 1000
Case "D"
Dim intV As Integer
intV = .Offset(0, 9) + CInt(strI(ri.Qty)) / 1000
.Offset(0, 10) = (.Offset(0, 10) * .Offset(0, 9) + CInt(strI(ri.Qty)) / 1000 * CSng(strI(ri.Price))) / intV
.Offset(0, 9) = intV
Case "P"
.Offset(0, 6) = CSng(strI(ri.Price))
Case Else
工作表1.tbxR.Text = 工作表1.tbxR.Text & vbCrLf & "異常回報:" & strI(ri.sType)
End Select
End With
End Sub
'修改回報
Private Sub AlterRow(strI)
With [a1].Offset(WorksheetFunction.Match(strI(ri.KeyNo), Range([a2], [a1048576].End(xlUp)), 0), 0)
.Offset(0, 11) = CDate(strI(ri.tTime))
Select Case strI(ri.sType)
Case "N"
.Offset(0, 1) = strI(ri.OrderNo)
.Offset(0, 2) = strI(ri.ComId)
.Offset(0, 3) = Code2Name(strI(ri.ComId))
.Offset(0, 4) = Mid(strI(ri.BuySell), 1, 1)
.Offset(0, 5) = CDate(strI(ri.tTime))
.Offset(0, 6) = CSng(strI(ri.Price))
.Offset(0, 7) = CInt(strI(ri.Qty)) / 1000
Case "C", "U"
.Offset(0, 8) = CInt(strI(ri.Qty)) / 1000
Case "D"
Dim intV As Integer
intV = .Offset(0, 9) + CInt(strI(ri.Qty)) / 1000
.Offset(0, 10) = (.Offset(0, 10) * .Offset(0, 9) + CInt(strI(ri.Qty)) / 1000 * CSng(strI(ri.Price))) / intV
.Offset(0, 9) = intV
Case "P"
.Offset(0, 6) = CSng(strI(ri.Price))
Case Else
工作表1.tbxR.Text = 工作表1.tbxR.Text & vbCrLf & "異常回報:" & strI(ri.sType)
End Select
End With
End Sub
'傳回股名
Private Function Code2Name(strCode) As String
Dim strName As String
Dim rngCode As Range
Set rngCode = ThisWorkbook.Names("報價").RefersToRange.Offset(0, -1).Resize(, 1)
If WorksheetFunction.CountIf(rngCode, strCode) > 0 Then
strName = 工作表1.[b1].Offset(WorksheetFunction.Match(strCode, rngCode, 0), 0)
Else
strName = "Unknown"
End If
Code2Name = strName
End Function
Private Sub skR_OnReplyMessage(ByVal bstrUserID As String, ByVal bstrMessage As String, sConfirmCode As Integer)
sConfirmCode = True
'MsgBox (bstrMessage)
End Sub
Sub connect_reply()
If [a1048576].End(xlUp).Row > 1 Then
Range([a2], [a1048576].End(xlUp)).EntireRow.ClearContents
End If
Call skR.SKReplyLib_ConnectByID(工作表1.strID)
End Sub

UserForm1

Private Sub btnCancel_Click()
Call Unload(Me)
End Sub
Private Sub btnOK_Click()
工作表1.strID = tbxID.Text
工作表1.strPW = tbxPW.Text
Call Unload(Me)
End Sub