上文介紹了Excel中的UDF函數,本文介紹一下同樣重要的RTD函數。從Excel 2002開始,Excel引入了一種新的查看和更新實時數據的機制,即real-time data簡稱RTD函數,他是一種Push-Pull的方式,及在需要更新數據的時候,RTD給Excel Push一個消息說要更新數據,Excel在收到消息後主動拉取Pull新的數據。RTD函數最開始的用途在於更新實時變化的數據,比如股票實時行情數據,實時天氣預報數據,球隊比賽得分數據等等。
在過去,要實現這些功能,需要依賴一些其他諸如Dynamic Data Exchange(DDE)技術來訪問實時數據資源,但DDE和標准的Excel函數樣式有很大的不同,並且並不是為Excel獲取實時數據而設計的,缺乏健壯性,並且效率不高,RTD的引入解決了這些問題。
本文首先介紹RTD的一些常用的使用場景,RTD函數的基本結構,注意事項,最後演示如何通過RTD函數來實現從Google Financial API中獲取實時行情數據。
RTD函數很有用,如果您遇到以下情況,那麼您應當考慮使用RTD函數了:
Excel RTD函數是一個實現了IRtdServer接口的Com組件,Excel通過該Com組件與實時數據進行交互。要實現RTD 函數,必須要實現IRtdServer這一接口,該接口位於 Microsoft.Office.Interop.Excel命名空間中,跳轉到定義,可以看到該接口的內部:
[()]
[(4160)]
{
[(11)]
ConnectData(TopicID, Strings, GetNewValues);
[(13)]
DisconnectData(TopicID);
[(14)]
Heartbeat();
[(12)]
RefreshData(TopicCount);
[(10)]
ServerStart(CallbackObject);
[(15)]
ServerTerminate();
}
其中ServerStart參數中有類型為IRTDUpdateEvent接口,該接口的實現為:
[()]
[(4160)]
{
[(11)]
HeartbeatInterval { ; ; }
[(12)]
Disconnect();
[(10)]
UpdateNotify();
}
巨硬的接口注釋寫的很清楚,不過這裡還是逐條解釋一下,先看IRtdServer接口,這裡依照一般的執行順序講解:
上面的接口介紹完了,現在介紹下IRTDUpdateEvent接口,該接口比較重要的一個方法是UpdateNotify方法。該方法想Excel發出一個通知,提示有新數據需要更新,這是Excel就會調用RefreshData方法,從中讀取到更新後的數據。需要注意的是調用UpdateNofity方法必須要在Excel主線程中進行。
上面簡單講解了RTD函數的基本原理,接下來演示如何通過Excel RTD來實現從Google Fiancial API中獲取實時行情並刷新數據,Google Financal API,提供了世界各大交易所的實時行情數據,其支持的市場及時效性在其官網上有說明,其使用方法可以參考這篇文章,這裡不細談。其主要思路如下,首先定義好請求的參數,我們的實時行情請求參數有兩個,一個是股票代碼,一個是指標的名稱。因為我們請求的是http,而不是注冊事件回調的方式,所以需要在RTD中使用timer控件去主動拉去請求,然後請求處理完成之後,將結果存儲到對象中,然後調用UpdateNotify方法通知Excel來更新。RTD函數其實是一個注冊為Com組件的類庫,所以我們首先創建一個名為YYGoogleFinancialRTD的類庫:
3.2 創建RTD
如何創建RTD函數是本文的重點,這裡我們先新建一個名為FinancialRtd的類,然後讓他實現IRtdServer接口。利用VS的自動完成,實現其五個方法。和使用.NET 編寫UDF 一樣,我們需要在類名稱上加一些自定義屬性。這四個屬性中,與UDF相比多了一個ProgID屬性,該屬性唯一標識改RTD函數。
[()]
[()]
[(.AutoDual)]
[()]
: {
xlRTDUpdate;
tmrTimer;
<> stockDatas;
gfinancial = ;
}
在實現五個方法之前,我們需要定義一些局部變量,第一個變量是xlRTDUpdate對象,該對象用來保存ServerStart中傳進來的對象的引用,以方便後面調用該對象的UpdateNotify方法。Timer控件用來定時的從http接口中獲取實時行情數據,List<RealStockData>對象用來保存所有的請求及其返回值。Gfinancial對象用來獲取從Google Financial API中獲取的實時行情。由於當前不再交易時間,所以我在收盤價格的基礎上加了一個Random來演示實時變化。這個在下載的代碼中您可以看到。這裡不多講。定義好這些變量之後我們就可以開始編寫代碼了。
第一個要實現的方法是 ServerStart方法。在該方法中,我們將CallbackObject對象保存到之前定義好的局部變量中,供日後調用該對象的UpdateNotify方法。然後,我們初始化了Timer對象,將其設置為2s去獲取一次實時行情數據。最後返回1表示RTD服務正常開啟。timer的Elapsed方法我們後面介紹。
ServerStart(CallbackObject)
{
xlRTDUpdate = CallbackObject;
gfinancial = ();
tmrTimer = ();
tmrTimer.Interval = 2000;
tmrTimer.Elapsed += tmrTimer_Elapsed;
1;
}
第二個要實現的重要的方法是ConnectData方法:
ConnectData(TopicID, Strings, GetNewValues)
{
GetNewValues = ;
strStockCode = Strings.GetValue(0).ToString().ToLower();
strIndex = Strings.GetValue(1).ToString();
{
(stockDatas == )
{
stockDatas = <>();
}
temp = ();
temp.StockCode = strStockCode;
temp.Index = strIndex;
gfinancial.GetRealStock(temp);
(temp.Value != )
{
stockDatas.Add(temp);
}
}
{
;
}
(!tmrTimer.Enabled)
{
tmrTimer.Start();
}
(i = 0; i < stockDatas.Count; i++)
{
(stockDatas[i].StockCode.Equals(strStockCode, .OrdinalIgnoreCase) && stockDatas[i].Index.ToString().Equals(strIndex, .OrdinalIgnoreCase))
{
(stockDatas[i].TopicId == -1)
{
stockDatas[i].TopicId = TopicID;
}
stockDatas[i].Value;
}
}
;
}
在該方法中,我們首先解析傳進來的參數,根據之前的約定,第一個參數為股票代碼,第二個參數為指標名稱。然後我們實例化了一個RealStockData對象,並給該對象的相關StockCode和Index賦值,然後去為該請求去請求一次實時行情,並將值存儲到對象的Value屬性中。然後將該請求加載到List集合對象中。最後循環判斷是否已經存在,如果已經存在,則直接返回值,否則將該次請求的Excel為其分配的TopicID存到該次對象的TopicId對象中。以便後面刷新時使用。最後如果輸入的參數條件不滿足要求,提示用戶請求格式不正確。每一個單元格的請求僅執行該方法一次。後面就通過刷新機制實現更新了。
為了連續性,現在介紹timer的Elapse方法,該方法的實現如下:
tmrTimer_Elapsed(sender, e)
{
gfinancial.GetRealStock(stockDatas);
xlRTDUpdate.UpdateNotify();
}
該方法很簡單,第一句去講我們之前保存的List集合的所有請求那過去請求實時行情,並將其返回值,保存到各元素的Value中,然後調用UpdateNotify方法通知Excel新的數據值已經獲取到了,新數據的值就存在stockDatas的各元素的Value屬性中。這時Excel收到UpdateNotify方法之後,就回去調用RefreshData方法,該方法的實現如下:
RefreshData(TopicCount)
{
[,] rets = [2, stockDatas.Count];
counter = 0;
(data stockDatas)
{
(data.TopicId != -1)
{
rets[0, counter] = data.TopicId;
rets[1, counter] = data.Value;
}
counter++;
}
TopicCount = stockDatas.Count;
rets;
}
該方法很簡單,首先我們創建了一個二維數組,第二維的大小即為所有有效請求的個數,這裡幾位stockDatas元素的個數。
然後遍歷所有的請求,填充該二維數組,第一維值為TopicID,Excel會通過該ID去更新對應的單元格,第二維為單元格的值,即將該值填充到對應的TopicID中。另外我們還要通知Excel我們要請求刷新的單元格的個數,這個個數當然就是所有請求的個數啦。最後我們返回這個二位數組。
至此,最重要的幾個方法介紹完了。
和ConnectData方法對應的DisconnectData方法在我們在Excel中刪除我們之前輸入的RTD函數時觸發,方法實現如下,操作就是從我們的所有請求集合中移除該TopicID對應的請求。
DisconnectData(TopicID)
{
(i = stockDatas.Count - 1; i > 0; i--)
{
(stockDatas[i].TopicId == TopicID)
{
stockDatas.RemoveAt(i);
}
}
((stockDatas == || stockDatas.Count == 0) && tmrTimer.Enabled)
{
tmrTimer.Stop();
}
}
Heartbeat方法僅僅返回1,表示我們的RTD還在運行中。
Heartbeat()
{
1;
}
最後和ServerStart方法對應的方法ServerTerminate在關閉該RTD工作表時觸發,該方法的主要用來釋放資源,其實現如下:
ServerTerminate()
{
xlRTDUpdate = ;
(tmrTimer.Enabled)
{
tmrTimer.Stop();
}
tmrTimer.Elapsed -= (tmrTimer_Elapsed);
tmrTimer.Dispose();
}
至此,我們的函數編寫完了。
完成之後,和UDF函數一樣,我們需要將該類庫注冊為Com組件,在Visual Studio編譯時勾選注冊為Com組件即可,提醒您的是,在Windows 7 及以上系統上,注冊Com組件涉及到要往注冊表些東西,所以需要當前的Visual Studio以管理員權限運行。
本文介紹了Excel中一類非常重要的函數,及RTD函數,他是Excel提供的一種Push-Pull機制的數據更新機制,在一些對數據實時性要求較高的場合,比如直播比賽得分,實時天氣預報,交易所實時行情等方面用的很多。並且通過RTD的這種機制我們可以在此基礎上實現異步的UDF函數,即當請求發進來的時候,我們暫不處理,記下TopicID,然後放到處理隊列中,待處理完成後,調用UpdateNotify方法,然後刷新已經處理完的TopicID的單元格。下文將會介紹Excel中異步自定義函數,異步的UDF函數能夠極大地提高Excel插件的用戶體驗,能夠提高系統的可擴展性和穩定性。
本文所有的代碼點擊此處下載,希望本文對您了解Excel中的RTD函數有所幫助。