我最近正在寫一個成交量判讀的應用程式。 要能即時判斷構成大盤主要的權值成份股 (約 60~80 家即可),在任一權值股跳動時 (Tick),就立即加總成交量,並在每一分鐘報出 (report) 買進價量 (BidPrice Volume) 或為 賣出價量 (AskPrice Volume) 的相對數據。

以 eLeader 來說,視窗 [5203] 就是期貨的成份股即時報價,用戶若要判斷個別的權值股單量是買進價量或賣出價量很簡單,報價視窗在 "單量" 這一欄,當出現紅色數字時代表 "買進價量";相對來說,出現綠色數字則為 "賣出價量"。 (還有一種罕見的黑色,算是無法判斷為買 or 賣價量)

但是若要寫成應用程式時,程式可不知道紅色或綠色之分。 理論上,在該看盤視窗上,應該會有個 "Flag" 旗標欄位,透過所提供的數據 (如 1為紅、2為綠),來協助判讀 單量 為 買或賣 價量。 但是 eLeader 不知怎麼搞的,可能是 Bug,並沒有這個 Flag 欄位。 所以要即時判斷單量的顏色,該邏輯就變得相對複雜很多。

生魚片給了我一份「單量顏色判斷原則」參考。 最重要的判讀原則是,程式一定要保存著個股前一個 Tick 的買進價、賣出價與成交價,再與現在 Tick 的買進、賣出、成交價等來比較。 其判斷的邏輯參考如下:

時間 買進 賣出 成交 單量
t B2 A2 P2 V2
t-1 B1 A1 P1 V1

-成交明細只揭露有成交的資訊.
-交易所所送資訊,是以 B2/A2/P2為一組資訊.B1/A1為成交前的買賣價,B2/A2為成交後的買賣價.

-單量判斷原則:
(1)僅有買價或賣價:

時間 買進 賣出 成交 單量
t B2 --- P2 V2

當只有買進價,若P2=B2,則V2是紅色(因為市場上都沒有人要賣);

時間 買進 賣出 成交 單量
t --- A2 P2 V2

當只有賣出價時,若P2=A2,則V2是綠色(因為市場上都沒有人要買);

(2)把成交後的買賣價資訊與成交前的買賣價比較:
若B2=A1,則V2是紅色(以較高的價格成交表示買方向賣方妥協,願意以較高的價格買);
若A2=B1,則V2是綠色(以較低的價格成交表示賣方向買方妥協,原意降低價格求售);

(3)若不符合上列條件,以同一時段的買賣價做比較:
若P2>=A1,則V2是紅色;
若P2<=B1,則V2是綠色; (4)若同時不符合(1)(2)(3)時: 也就是B2不等於A1,A2不等於B1,P2不等於B1,P2不等於A1,則V2的顏色視P2的價位較P1大小決定, 若P2>P1,V2標紅色;
若P2=P1,V2標黑色;
若P2

然後我把其邏輯寫進 C# 的程式碼,其片段原始碼參考如下:
(目前測試結果,大概 100 次的 Tick 跳動,好像還是有 1~2 次的顏色判讀錯誤情形,不知道問題在哪裡。 不過大致上運作都算正常,算是在可容許範圍之內。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
 //判斷 Item 名稱 是否為 "ExecutionVol"
 if (args.Item != "ExecutionVol")
 {
     advised_topic.updateItemData(args.Item, data);
 
     //儲存前一個 Tick 的成交、買賣量價
     if (args.Item == "CurPrice")
     {
         prev_買賣價.price = Math.Round(Convert.ToDouble(advised_topic.getItemData("CurPrice")), 2);
     }
     else if (args.Item == "BidPrice1")
     {
         prev_買賣價.bidprice1 = Math.Round(Convert.ToDouble(t2_topic.getItemData("BidPrice1")), 2);
     }
     else if (args.Item == "AskPrice1")
     {
         prev_買賣價.askprice1 = Math.Round(Convert.ToDouble(t2_topic.getItemData("AskPrice1")), 2);
     }
 }
 else
 {
     /**
     //判斷是否為 買量 or 賣量
     int execVol = Convert.ToInt16(data); //單量
     int ticktype = Convert.ToInt16((t_topic.getItemData("TickType")));
 
     // TickType: 1.紅: 2.綠; 3.黑                                       
     switch (ticktype)
     {
         case 1:
             itss.累積買價量 = itss.累積買價量 + execVol;
             execVol = Math.Abs(execVol);
             break;
         case 2:
             itss.累積賣價量 = itss.累積賣價量 + execVol;
             execVol = (Math.Abs(execVol)) * -1;
             break;
         case 3:
             execVol = 0;
             break;
     }
      */
 
     /** 時間 	  買進      賣出      成交      單量
         t           B2        A2        P2        V2
         t-1         B1        A1        P1        V1
     */
 
     int v2 = Math.Abs(Convert.ToInt16(data)); //單量
 
     //取得 同一個 StockID 內,所需要判斷處理的其它 Items 資料
     double p2 = Math.Round(Convert.ToDouble(advised_topic.getItemData("CurPrice")), 2);
     double b2 = Math.Round(Convert.ToDouble(t2_topic.getItemData("BidPrice1")), 2);
     double a2 = Math.Round(Convert.ToDouble(t2_topic.getItemData("AskPrice1")), 2);
 
     //前一個 Tick 的賣賣價;
     double b1 = prev_買賣價.bidprice1;
     double a1 = prev_買賣價.askprice1;
     double p1 = prev_買賣價.price;
 
     //第一盤
     if ((a1 == 0) && (b1 == 0))
     {
         v2 = 0;
         if (p2 >= a2)
         {
             itss.累積買價量 = itss.累積買價量 + v2;
         }
         else
         {                                                
             itss.累積賣價量 = itss.累積賣價量 + v2;
             v2 = -v2;
         }
     }
     else if ((a2 == 0) || (b2 == 0))
     {
         if (a2 == 0 && b2 == 0)
             v2 = 0;
         // 當只有買進價,若P2=B2,則V2是紅色(因為市場上都沒有人要賣)
         else if (a2 == 0 && p2 == b2)
         {
             itss.累積買價量 = itss.累積買價量 + v2;
         }
         //當只有賣出價時,若P2=A2,則V2是綠色(因為市場上都沒有人要買)
         else if (b2 == 0 && p2 == a2)
         {
             itss.累積賣價量 = itss.累積賣價量 + v2;
             v2 = -v2;
         }
     }
 
     //把成交後的買賣價資訊與成交前的買賣價比較:
     //若B2=A1,則V2是紅色(以較高的價格成交表示買方向賣方妥協,願意以較高的價格買);
     //若A2=B1,則V2是綠色(以較低的價格成交表示賣方向買方妥協,原意降低價格求售);
     else if ((b2 == a1) || (a2 == b1))
     {
         if (b2 == a1)
         {
             itss.累積買價量 = itss.累積買價量 + v2;
         }
         else if (a2 == b1)
         {
             itss.累積賣價量 = itss.累積賣價量 + v2;
             v2 = -v2;
         }
     }
     // 若不符合上列條件,以同一時段的買賣價做比較:
     // 若P2>=A1,則V2是紅色;
     //若P2<=B1,則V2是綠色;
     else if ((p2 >= a1) || (p2 <= b1))
     {
         if (p2 >= a1)
         {
             itss.累積買價量 = itss.累積買價量 + v2;
         }
         else if (p2 <= b1)
         {
             itss.累積賣價量 = itss.累積賣價量 + v2;
             v2 = -v2;
         }
     }
     else
     {
         if (p2 > p1)
         {
             itss.累積買價量 = itss.累積買價量 + v2;
         }
         else if (p2 == p1)
         {
             v2 = 0; //不計量
         }
         else if (p2 < p1)
         {
             itss.累積賣價量 = itss.累積賣價量 + v2;
             v2 = -v2;
         }
     }
 
     advised_topic.updateItemData(args.Item, v2.ToString());
 }