Excel RTD(Excel Real-Time Data)实时刷新数据技术

清泛编译

从Excel 2002开始,微软提供了一种叫做Excel RTD(real-time data)的技术,使用该技术可以在Excel中实时查看和更新数据。RTD采用所谓的“推-拉”相结合的技术,使得其在实时获取和更新不断变化的数据(例如股票、汇率、天气)的性能方面,相比较之前的DDE更加稳健和快速。在MSDN的Real-Time Data FAQ上有其性能描述(http://msdn.microsoft.com/en-us/library/aa140060(v=office.10).aspx#odc_xlrtdfaq_whatisrtd),据说在一台配置为 Pentium III 500 MHz 的CPU和 128 MB 内存的电脑上,RTD可以在一秒内对20000个独立的主题(topic)更新三次,对于单独一个主题的更新频率可达1秒200次。

使用场景

    RTD函数很有用,如果您遇到以下情况,那么您应当考虑使用RTD函数了:

  1. 实时数据更新:这是很常见遇到的一种情景,比如说实时行情,实时天气情况,直播的比赛得分情况等。
  2. 一些特殊的场景,比如说证券交易公司的交易系统,这些公司的某些模型会基于实时的行情或者指数进行计算,通过RTD获取这些基础数据的实时数据之后,在此基础上可以进行动态的建模和分析。
  3. 异步的,耗时的数据请求:传统的UDF函数存在的一个问题是,他是同步的,就是说,如果某个函数的执行时间过长,会导致前端界面的卡顿,尤其是在UDF函数访问数据库,WebService,或复杂的计算过程等不确定性及耗时的执行环境时。基于RTD技术可以实现异步的UDF函数,从而带来更好的用户体验。

在Excel 中使用RTD非常简单,Excel 提供了一个新的工作表函数 RTD,此函数允许通过调用组件对象模型 (COM) 自动化服务器来实现实时数据检索。RTD 工作表函数使用以下语法:

    "=RTD(ProgID, Server, String 1, String 2, ... String n)"

第一个变量 ProgID 表示Real-Time Data 服务器(RTD Server)的编程标识符 (ProgID)。Server 变量指示运行RTD Server的计算机的名称;如果RTD Server在本地运行,则可以将此变量设置为空字符串或将其忽略。其他变量只表示发送到RTD Server的参数;这些参数的每个唯一组合都表示一个“主题”(topic),每个“主题”有一个关联的“主题 ID”(topic id)。这些参数区分大小写。例如,以下内容演示将生成三个不同主题ID的RTD Server调用:

=RTD("ExcelRTD.RTDFunctions",,"AAA", "10")
=RTD("ExcelRTD.RTDFunctions",,"AAA", "5")
=RTD("ExcelRTD.RTDFunctions",,"aaa", "5")

要使用 Excel 的 RTD 函数,必须注册一个实现 IRTDServer 接口的COM组件。实现这个接口的COM组件就是所谓的RTD Server。IRTDServer具有以下成员:

ServerStart(CallbackObject)

CallbackObject 是一个IRTDUpdateEvent类型的参数,它有一个UpdateNotify方法,用于通知Excel有更新的数据可用(push)。这样Excel就会通过调用RefreshData方法来刷新所有的主题(pull)。当 Excel 请求RTD Server的第一个 RTD 主题时调用ServerStart方法,该方法会在成功时返回 1,并在失败时返回负值或 0。这个方法在随后应用其他RTD函数时不会再次被调用。

ConnectData(TopicID, Strings, GetNewValues)

其中,TopcID 唯一标识这个函数在Excel中的一个应用,即使复制多份到不同的单元格,对于Excel来讲,也只是对应一个主题。这个 topicID 由Excel返回,我们需要将其记录下来,以便为其提供更新的数据。Strings 是一个System.Array,用于接收RTD函数传入的参数(String 1...String n),这是一个引用类型的参数。GetNewValues用于确定是否总是获取最新数据,如果这个参数传入true,则每次保存Excel文档以后,再次重新打开时,看到的不一定是上次保存时的数据,而是最新的实时数据,这也是一个引用类型的参数。

每当一个新的主题(Topic)被应用到Excel,ConnectData都会被调用。在这里,需要保存传入的新的TopicID和查询参数以供之后更新数据使用。为此,需要定义好自己的数据结构。

DisconnectData(TopicID)

与ConnectData一样,TopcID 唯一标识这个函数在Excel中的一个应用。当我们从Excel中移除一个主题(删除所有采用相同参数的RTD函数)之后,DisconnectData将被调用,在这里,可以释放对这个主题的监控,并不再为其获取新数据。

Heartbeat

确定RTD Server是不是依然可用,0和负数代表不可用,1代表可用。Excel会调用此方法确定服务是否断连。

RefreshData(TopicCount)

TopicCount表示要更新的主题数量,这是一个引用类型的参数,用于返回给Excel。我们可以定义一个时钟,用于定时向数据源获取数据,这样,在时钟的Elapsed事件中,获取最新数据,并调用xlRTDUpdate成员的UpdateNotify方法以通知Excel,新的数据准备完毕。这样Excel就会调用RefreshData方法,来对工作簿中的数据进行更新。

ServerTerminate

当Excel不再需要从RTD Server获取实时数据时被调用。在这里,可以执行一些清理,例如清除缓存,关闭时钟等等。至此,一个RTD Server的生命周期就结束了。
 

一 C#版实例

来源:http://www.cnblogs.com/makemelaugh/archive/2008/11/06/1327960.html

创建一个项目ExcelRTD,添加Microsoft.Office.Interop.Excel引用。创建一个类MarketData.cs,这个类继承IRtdServer接口,以实现一个RTD Server。新建另一个类DataCollector.cs用于从数据源获取数据。DataCollector有一个方法public DataTable GetMarketData(string keyField, object keyValue)将会在MarketData.cs中被使用。为了图简便,我就贴出MarketData.cs的全部代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Collections;
using System.Configuration;
using System.Timers;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;

namespace ExcelRTD
{
    [ComVisible(true),ProgId("ExcelRTD.MarketData")]
    public class MarketDataServer : IRtdServer 
    {
        private IRTDUpdateEvent xlRTDUpdate;
        private Timer tmrTimer;
        private List<MarketData> marketDatas;

        //Lets Excel know that our RTD server is still "alive".
        public int Heartbeat()
        {
            return 1;
        }
        
        //Initialize RTD server.
        public int ServerStart(Microsoft.Office.Interop.Excel.IRTDUpdateEvent CallbackObject)
        {
            //Hold a reference to the callback object.
            xlRTDUpdate = CallbackObject;

            //2000 millisecond by default.
            int intv = 2000;

            //'Create the time with a 5000 millisecond interval.
            tmrTimer = new Timer(intv);
            tmrTimer.AutoReset = true;

            tmrTimer.Elapsed += new ElapsedEventHandler(tmrTimer_Elapsed);

            return 1;
        }

        //Terminate RTD server.
        public void ServerTerminate()
        {
            //Clear the RTDUpdateEvent reference.
            xlRTDUpdate = null;

            //Make sure the timer is stopped.
            if (tmrTimer.Enabled)
            {
                tmrTimer.Stop();
            }

            tmrTimer.Elapsed -= new ElapsedEventHandler(tmrTimer_Elapsed);
            tmrTimer.Dispose();
        }


        public object ConnectData(int TopicID, ref System.Array Strings, ref bool GetNewValues)
        {
            //Retrive new values from RTD server when connected.
            GetNewValues = true;

            //Get field name from Excel.
            string strFieldName = Strings.GetValue(0).ToString().ToLower();
            string strKeyValue = Strings.GetValue(1).ToString();

            try
            {
                if (marketDatas == null)
                {
                    marketDatas = new List<MarketData>();
                }

                //Get real-time data from data source.
                DataCollector dc = new DataCollector();
                System.Data.DataTable dt = null;

                MarketData temp = new MarketData();

                temp.KeyField = "stockcode";
                temp.KeyValue = strKeyValue;
                temp.FieldName = strFieldName;

                dt = dc.GetMarketData(temp.KeyField, temp.KeyValue);

                if (dt != null && dt.Rows.Count > 0)
                {
                    temp.FieldValue = dt.Rows[0][temp.FieldName];

                    marketDatas.Add(temp);
                }   

            }
            catch
            {
                return "ERROR IN QUOTE.";
            }

            //Make sure that the timer is started.
            if (!tmrTimer.Enabled)
            {
                tmrTimer.Start();
            }

            for (int i = 0; i < marketDatas.Count; i++)
            {
                //Match the topic value
                if (marketDatas[i].FieldName.Equals(strFieldName, StringComparison.OrdinalIgnoreCase) && marketDatas[i].KeyValue.ToString().Equals(strKeyValue, StringComparison.OrdinalIgnoreCase))
                {
                    if (marketDatas[i].TopicID == -1)
                    {
                        marketDatas[i].TopicID = TopicID;
                    }

                    return marketDatas[i].FieldValue;
                }
            }

            return "Unrecognized value requested";
        }

        public void DisconnectData(int TopicID)
        {
            for (int i = marketDatas.Count - 1; i > 0; i--)
            {
                if (marketDatas[i].TopicID == TopicID)
                {
                    marketDatas.RemoveAt(i);
                }
            }

            if ((marketDatas == null || marketDatas.Count == 0) && tmrTimer.Enabled)
            {
                tmrTimer.Stop();
            }
        }


        public void tmrTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                for (int i = 0; i < marketDatas.Count; i++)
                {
                    DataCollector dc = new DataCollector();
                    System.Data.DataTable dt = dc.GetMarketData(marketDatas[i].KeyField, marketDatas[i].KeyValue);

                    if (dt != null && dt.Rows.Count > 0)
                    {
                        marketDatas[i].FieldValue = dt.Rows[0][marketDatas[i].FieldName];
                    }
                }

                //Tell Excel that we have updates. 
                xlRTDUpdate.UpdateNotify();
            }
            catch(Exception ex)
            {
                string x = ex.ToString();
            }
        }

        //Pull new values from the real-time data server to Excel.
        public System.Array RefreshData(ref int TopicCount)
        {
            object[,] rets = new object[2, marketDatas.Count];
            int counter = 0, sum = 0;

            foreach (MarketData data in marketDatas)
            {
                if (data.TopicID != -1)
                {
                    rets[0, counter] = data.TopicID;
                    rets[1, counter] = data.FieldValue;

                    sum++;
                }

                counter++;
            }

            TopicCount = marketDatas.Count;

            return rets;
        }


        private class MarketData
        {
            private string fieldName;
            private object fieldValue;
            private string keyField;
            private object keyValue;
            private int topicID;

            public MarketData()
            {
                topicID = -1;

                keyValue = null;
                fieldValue = null;
            }

            public string FieldName
            {
                get
                {
                    return fieldName;
                }
                set
                {
                    fieldName = value;
                }
            }

            public object FieldValue
            {
                get
                {
                    return fieldValue;
                }
                set
                {
                    fieldValue = value;
                }
            }

            public string KeyField
            {
                get
                {
                    return keyField;
                }
                set
                {
                    keyField = value;
                }
            }

            public object KeyValue
            {
                get
                {
                    return keyValue;
                }
                set
                {
                    keyValue = value;
                }
            }

            public int TopicID
            {
                get
                {
                    return topicID;
                }
                set
                {
                    topicID = value;
                }
            }
        }
    }
}

使用时,需要注册COM。这只是一个简单的示例。

Step by step可以参见以下链接:

http://msdn.microsoft.com/en-us/library/aa140061(office.10).aspx

http://support.microsoft.com/kb/285339/zh-cn

MSDN上的文章虽然是针对Excel 2002,但是2003和2007同样支持。此外,从外部数据源获取Excel的方法有很多种,这只是其中一种而已,它并不能适应所有的情况。
 



二 C++ COM基本结构

Excel RTD函数是一个实现了IRtdServer接口的Com组件,Excel通过该Com组件与实时数据进行交互。要实现RTD 函数,必须要实现IRtdServer这一接口,该接口位于 Microsoft.Office.Interop.Excel命名空间中,跳转到定义,可以看到该接口的内部:

/// <summary>
///  Represents an interface for a real-time data server.
/// </summary>
[Guid("EC0E6191-DB51-11D3-8F3E-00C04F3651B8")]
[TypeLibType(4160)]
public interface IRtdServer2
{
    /// <summary>
    /// Adds new topics from a real-time data server. The ConnectData method is called
    ///     when a file is opened that contains real-time data functions or when a user
    ///     types in a new formula which contains the RTD function.
    /// </summary>
    /// <param name="TopicID">
    /// Required Integer. A unique value, assigned by Microsoft Excel, which identifies the topic.</param>
    /// <param name="Strings">Required Object. A single-dimensional array of strings identifying the topic.</param>
    /// <param name="GetNewValues"> Required Boolean. True to determine if new values are to be acquired.</param>
    /// <returns></returns>
    [DispId(11)]
    dynamic ConnectData(int TopicID, ref Array Strings, ref bool GetNewValues);

    /// <summary>
    /// Notifies a real-time data (RTD) server application that a topic is no longer in use.
    /// </summary>
    /// <param name="TopicID"> Required Integer. A unique value assigned to the topic assigned by Microsoft Excel.</param>
    [DispId(13)]
    void DisconnectData(int TopicID);
    [DispId(14)]
    int Heartbeat();
       
    /// <summary>
    /// This method is called by Microsoft Excel to get new data.
    /// </summary>
    /// <param name="TopicCount">TopicCount:
    ///     Required Integer. The RTD server must change the value of the TopicCount
    ///     to the number of elements in the array returned.</param>
    /// <returns></returns>
    [DispId(12)]
    Array RefreshData(ref int TopicCount);
       
    /// <summary>
    /// The ServerStart method is called immediately after a real-time data server
    ///     is instantiated. Negative value or zero indicates failure to start the server;
    ///     positive value indicates success.
    /// </summary>
    /// <param name="CallbackObject">Required Microsoft.Office.Interop.Excel.IRTDUpdateEvent object. The callback object.</param>
    /// <returns></returns>
    [DispId(10)]
    int ServerStart(IRTDUpdateEvent CallbackObject);
         
    /// <summary>
    /// Terminates the connection to the real-time data server.
    /// </summary>
    [DispId(15)]
    void ServerTerminate();
}

其中ServerStart参数中有类型为IRTDUpdateEvent接口,该接口的实现为:

/// <summary>
/// Represents real-time data update events.
/// </summary>
[Guid("A43788C1-D91B-11D3-8F39-00C04F3651B8")]
[TypeLibType(4160)]
public interface IRTDUpdateEvent
{
    [DispId(11)]
    int HeartbeatInterval { get; set; }

    /// <summary>
    ///   Instructs the real-time data server (RTD) to disconnect from the specified
    ///   Microsoft.Office.Interop.Excel.IRTDUpdateEvent object.
    /// </summary>
    [DispId(12)]
    void Disconnect();
    [DispId(10)]
    void UpdateNotify();
}

上面的接口介绍完了,现在介绍下IRTDUpdateEvent接口,该接口比较重要的一个方法是UpdateNotify方法。该方法想Excel发出一个通知,提示有新数据需要更新,这是Excel就会调用RefreshData方法,从中读取到更新后的数据。需要注意的是调用UpdateNofity方法必须要在Excel主线程中进行。

三 实例:RTD实时行情

上面简单讲解了RTD函数的基本原理,接下来演示如何通过Excel RTD来实现从Google Fiancial API中获取实时行情并刷新数据,Google Financal API,提供了世界各大交易所的实时行情数据,其支持的市场及时效性在其官网上有说明,其使用方法可以参考这篇文章,这里不细谈。其主要思路如下,首先定义好请求的参数,我们的实时行情请求参数有两个,一个是股票代码,一个是指标的名称。因为我们请求的是http,而不是注册事件回调的方式,所以需要在RTD中使用timer控件去主动拉去请求,然后请求处理完成之后,将结果存储到对象中,然后调用UpdateNotify方法通知Excel来更新。RTD函数其实是一个注册为Com组件的类库,所以我们首先创建一个名为YYGoogleFinancialRTD的类库:

Create RTD Class Liberary

3.1 创建实体类对象

要将我们的请求的结果保存起来,必须创建一个实体类,来保存TopicID,请求的指标,股票代码,以及返回值的信息,这样再刷新的时候可以根据这些信息来刷新。所以添加一个RealStockData类。

class RealStockData
{
    //该次请求的股票代码
    public string StockCode{get;set;}
    //该次请求的股票名称
    public string Index { get; set; }
    //该次请求Excel分配的TopicID
    public int TopicId { get; set; }
    //该次请求的返回值
    public object Value{get;set;}
}

3.2 创建RTD

如何创建RTD函数是本文的重点,这里我们先新建一个名为FinancialRtd的类,然后让他实现IRtdServer接口。利用VS的自动完成,实现其五个方法。和使用.NET 编写UDF 一样,我们需要在类名称上加一些自定义属性。这四个属性中,与UDF相比多了一个ProgID属性,该属性唯一标识改RTD函数。

[Guid("0A5F2820-C34E-4EF5-9110-F66B2A7796C0")]
[ProgId("YYRTD.Stock")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class FinancialRtd : IRtdServer
{
    private IRTDUpdateEvent xlRTDUpdate;
    private Timer tmrTimer;
    private List<RealStockData> stockDatas;
    private GoogleFinancial gfinancial = null;
}

在实现五个方法之前,我们需要定义一些局部变量,第一个变量是xlRTDUpdate对象,该对象用来保存ServerStart中传进来的对象的引用,以方便后面调用该对象的UpdateNotify方法。Timer控件用来定时的从http接口中获取实时行情数据,List<RealStockData>对象用来保存所有的请求及其返回值。Gfinancial对象用来获取从Google Financial API中获取的实时行情。由于当前不再交易时间,所以我在收盘价格的基础上加了一个Random来演示实时变化。这个在下载的代码中您可以看到。这里不多讲。定义好这些变量之后我们就可以开始编写代码了。

    第一个要实现的方法是 ServerStart方法。在该方法中,我们将CallbackObject对象保存到之前定义好的局部变量中,供日后调用该对象的UpdateNotify方法。然后,我们初始化了Timer对象,将其设置为2s去获取一次实时行情数据。最后返回1表示RTD服务正常开启。timer的Elapsed方法我们后面介绍。

public int ServerStart(IRTDUpdateEvent CallbackObject)
{
    xlRTDUpdate = CallbackObject;

    gfinancial = new GoogleFinancial();

    tmrTimer = new Timer();
    tmrTimer.Interval = 2000;
    tmrTimer.Elapsed += tmrTimer_Elapsed;

    return 1;
}

第二个要实现的重要的方法是ConnectData方法:

public dynamic ConnectData(int TopicID, ref Array Strings, ref bool GetNewValues)
{
    //Retrive new values from RTD server when connected.
    GetNewValues = true;

    //Get field name from Excel.
    string strStockCode = Strings.GetValue(0).ToString().ToLower();
    string strIndex = Strings.GetValue(1).ToString();

    try
    {
        if (stockDatas == null)
        {
            stockDatas = new List<RealStockData>();
        }

        //Get real-time data from data source.
        RealStockData temp = new RealStockData();

        temp.StockCode = strStockCode;
        temp.Index = strIndex;


        gfinancial.GetRealStock(temp);

        if (temp.Value != null)
        {
            stockDatas.Add(temp);
        }

    }
    catch
    {
        return "ERROR IN QUOTE.";
    }

    //Make sure that the timer is started.
    if (!tmrTimer.Enabled)
    {
        tmrTimer.Start();
    }

    for (int i = 0; i < stockDatas.Count; i++)
    {
        //Match the topic value
        if (stockDatas[i].StockCode.Equals(strStockCode, StringComparison.OrdinalIgnoreCase) && stockDatas[i].Index.ToString().Equals(strIndex, StringComparison.OrdinalIgnoreCase))
        {
            if (stockDatas[i].TopicId == -1)
            {
                stockDatas[i].TopicId = TopicID;
            }

            return stockDatas[i].Value;
        }
    }

    return "Unrecognized requested";
}

在该方法中,我们首先解析传进来的参数,根据之前的约定,第一个参数为股票代码,第二个参数为指标名称。然后我们实例化了一个RealStockData对象,并给该对象的相关StockCode和Index赋值,然后去为该请求去请求一次实时行情,并将值存储到对象的Value属性中。然后将该请求加载到List集合对象中。最后循环判断是否已经存在,如果已经存在,则直接返回值,否则将该次请求的Excel为其分配的TopicID存到该次对象的TopicId对象中。以便后面刷新时使用。最后如果输入的参数条件不满足要求,提示用户请求格式不正确。每一个单元格的请求仅执行该方法一次。后面就通过刷新机制实现更新了。

为了连续性,现在介绍timer的Elapse方法,该方法的实现如下:

private void tmrTimer_Elapsed(object sender, ElapsedEventArgs e)
{
    gfinancial.GetRealStock(stockDatas);

    xlRTDUpdate.UpdateNotify();
}

该方法很简单,第一句去讲我们之前保存的List集合的所有请求那过去请求实时行情,并将其返回值,保存到各元素的Value中,然后调用UpdateNotify方法通知Excel新的数据值已经获取到了,新数据的值就存在stockDatas的各元素的Value属性中。这时Excel收到UpdateNotify方法之后,就回去调用RefreshData方法,该方法的实现如下:

public Array RefreshData(ref int TopicCount)
{
    object[,] rets = new object[2, stockDatas.Count];
    int counter = 0;

    foreach (RealStockData data in stockDatas)
    {
        if (data.TopicId != -1)
        {
            rets[0, counter] = data.TopicId;
            rets[1, counter] = data.Value;
        }
        counter++;
    }

    TopicCount = stockDatas.Count;

    return rets;
}

该方法很简单,首先我们创建了一个二维数组,第二维的大小即为所有有效请求的个数,这里几位stockDatas元素的个数。

然后遍历所有的请求,填充该二维数组,第一维值为TopicID,Excel会通过该ID去更新对应的单元格,第二维为单元格的值,即将该值填充到对应的TopicID中。另外我们还要通知Excel我们要请求刷新的单元格的个数,这个个数当然就是所有请求的个数啦。最后我们返回这个二位数组。

至此,最重要的几个方法介绍完了。

和ConnectData方法对应的DisconnectData方法在我们在Excel中删除我们之前输入的RTD函数时触发,方法实现如下,操作就是从我们的所有请求集合中移除该TopicID对应的请求。

public void DisconnectData(int TopicID)
{
    for (int i = stockDatas.Count - 1; i > 0; i--)
    {
        if (stockDatas[i].TopicId == TopicID)
        {
            stockDatas.RemoveAt(i);
        }
    }

    if ((stockDatas == null || stockDatas.Count == 0) && tmrTimer.Enabled)
    {
        tmrTimer.Stop();
    }
}

Heartbeat方法仅仅返回1,表示我们的RTD还在运行中。

public int Heartbeat()
{
    return 1;
}

最后和ServerStart方法对应的方法ServerTerminate在关闭该RTD工作表时触发,该方法的主要用来释放资源,其实现如下:

public void ServerTerminate()
{
    //Clear the RTDUpdateEvent reference.
    xlRTDUpdate = null;

    //Make sure the timer is stopped.
    if (tmrTimer.Enabled)
    {
        tmrTimer.Stop();
    }

    tmrTimer.Elapsed -= new ElapsedEventHandler(tmrTimer_Elapsed);
    tmrTimer.Dispose();
}

至此,我们的函数编写完了。

完成之后,和UDF函数一样,我们需要将该类库注册为Com组件,在Visual Studio编译时勾选注册为Com组件即可,提醒您的是,在Windows 7 及以上系统上,注册Com组件涉及到要往注册表些东西,所以需要当前的Visual Studio以管理员权限运行。

Register Com

编译通过之后,我们可以直接打开Excel输入RTD函数了。

在Excel 中使用RTD非常简单,Excel 提供了一个新的工作表函数 RTD,此函数允许通过调用组件对象模型 (COM) 自动化服务器来实现实时数据检索。RTD 工作表函数使用以下语法:

  • =RTD(ProgID, Server, String 1, String 2, ... String n)

第一个变量 ProgID 表示Real-Time Data 服务器(RTD Server)的编程标识符 (ProgID),我们之前在RTD类的自定义属性中指定了ProgID,这里就可以用“YYRTD.Stock”了。Server 变量指示运行RTD Server的计算机的名称;如果RTD Server在本地运行,则可以将此变量设置为空字符串或将其忽略。后面的变量只表示发送到RTD Server的参数;这些参数的每个唯一组合都表示一个“主题”(topic),每个“主题”有一个关联的“主题 ID”(topic id)。这些参数区分大小写。例如,以下内容表示我们请求600000这只股票的最新价,最高价和最低价

  • =RTD("YYRTD.Stock",,"600000", "last")
  • =RTD("YYRTD.Stock",,"600000", "high")
  • =RTD("YYRTD.Stock",,"600000", "low")

我录制了一个动画,效果如下图,两秒钟刷新一次,由于当前不是交易时间,所以通过Google API获取的值是收盘时的行情,为了掩饰效果,我在原值的基础上加了一个随机变量以演示变化,如果在交易时间的话,您可以看到真实的股票行情变化的,相关代码您可以在附件中进行修改。

四 结语

本文介绍了Excel中一类非常重要的函数,及RTD函数,他是Excel提供的一种Push-Pull机制的数据更新机制,在一些对数据实时性要求较高的场合,比如直播比赛得分,实时天气预报,交易所实时行情等方面用的很多。并且通过RTD的这种机制我们可以在此基础上实现异步的UDF函数,即当请求发进来的时候,我们暂不处理,记下TopicID,然后放到处理队列中,待处理完成后,调用UpdateNotify方法,然后刷新已经处理完的TopicID的单元格。下文将会介绍Excel中异步自定义函数,异步的UDF函数能够极大地提高Excel插件的用户体验,能够提高系统的可扩展性和稳定性。

本文所有的代码点击此处下载,希望本文对您了解Excel中的RTD函数有所帮助。

来源:http://www.cnblogs.com/yangecnu/p/Excel-Realtime-Data-Function-Introduce.html

Excel RTD 实时刷新数据 Real-Time Data

分享到:
评论加载中,请稍后...
创APP如搭积木 - 创意无限,梦想即时!
回到顶部