利用 LINQ 實現期指Tick檔彙整為K棒

參考這一篇:「How to group a time series by interval (OHLC bars) with LINQ」。

我這裡使用 C#.NET 實作,並作了一些小小的修正,使之確實可以執行,並可從期交所所下載的原始 Tick 資料檔,經由前置的解壓縮、轉換為 Tick 物件型態,再透過該實作邏輯,就可以將 Tick 物件依所指定的時間週期 (time-interval),群組為所謂的「分K棒」(其實,正確的稱呼應該為 OHCK Bar)。

這裡也不禁對 LINQ 實作技術感到讚嘆! 它讓中間層 (middleware)的物件結構與資料來源 (data-sources)的存取,變得更為簡單太多了,所以也使得 Tick 轉K棒 的轉換邏輯,可以利用相當簡潔的語法 (類 SQL 語法),而達成該功能的實現。

底下的程式碼並未包含前述所提包括解壓縮、轉換為 Tick 物件等邏輯,只從一群已有 Tick 資料的 Collection 集合 (型態為 IEnumerable),依據所指定的 Interval 時間間隔,轉換為 K棒 (含開、高、收、低、量)的實作邏輯。

Tick 與 OHLC-Bar(K棒) 物件結構為:

    public class Tick
    {
        public string Symbol { get; set; }     //商品代號        
        public DateTime Timestamp { get; set; }  //成交日期時間
        public decimal Price { get; set; }     //成交價格
        public uint volume { get; set; }       //成交量
    }

    public class OHLCBar
    {
        public string Symbol { get; set; }  //商品代號
        public decimal OPEN { get;set; }  //開盤價
        public decimal CLOSE { get; set; }   //收盤價
        public decimal HIGH { get; set; }  //最高價
        public decimal LOW { get; set; }   //最低價
        public uint VOLUME { get; set; }   //成交量
        public DateTime BeginTime { get; set; } //開始時間
        public TimeSpan Interval { get; set; }   //時間區間
    }


將 Ticks 集合當成參數傳進該 method (generate_OHLCBar),處理後再傳回 OHLCBar 集合。

底下最精華的部分在於程式碼 21~29 行,這在傳統 SQL 語法是無法這樣寫出來的 (GroupBy 的欄位為變數),真的可以仔細玩味之該實作的技巧。


/** 將 Tick 資料依 Interval 時間間隔組成為 OHLC Bar.
 * <param name="ticks">Ticks 型態的集合</param>            
 */

private IEnumerable<OHLCBar> generate_OHLCBar(IEnumerable<Tick> ticks)
{
    long barSizeInTicks = 5;               //5 secs.
    TimeSpan natural_time = new TimeSpan(8,45,0);     //台指期開盤時間

    var query =
        from tick in ticks
        //取得 TimeBar 的起始索引值, index 值必須為整數
        let barIndexForDay = 
             Convert.ToInt32((tick.Timestamp.TimeOfDay.Subtract(natural_time)).TotalSeconds / barSizeInTicks)

        // Calculate the begin-time of the bar associated with a tick.
        // For example, turn 2011/04/28 14:23.45 
        // into 2011/04/28 14:20.00, assuming 5 min bars.

        let barBeginDateTime = 
              tick.Timestamp.Date.Add(natural_time).AddSeconds(barIndexForDay * barSizeInTicks)

        // Produce raw tick-data for each bar by grouping.
        group tick by new { barBeginDateTime, tick.Symbol } into tickGroup
        orderby tickGroup.Key.barBeginDateTime

        // Order prices for a group chronologically.
        let orderedPrices = tickGroup.OrderBy(t => t.Timestamp).Select(t => t.Price)

        select new OHLCBar()
        {
            Symbol = tickGroup.Key.Symbol,
            OPEN = orderedPrices.First(),
            CLOSE = orderedPrices.Last(),
            HIGH = orderedPrices.Max(),
            LOW = orderedPrices.Min(),
            BeginTime = tickGroup.Key.barBeginDateTime,
            VOLUME= Convert.ToUInt32(tickGroup.Sum(t => t.volume))
        };

    return query;
}

文章導覽

   

共有 4 則迴響

  1. 大大你好,在建構論文研究需要使用的系統時,進行到設計tick資料轉檔功能google到您這篇文章,簡潔的程式碼卻可以達到如此的功能,趕到相當興奮,於是在我的程式中立刻新增了一個tick.cs,並將您的程式碼貼過去,但接著馬上就呆了,以下是我思考的流程,盼望大大提點 :

    (在C#中)我已將期交所資料=>datatable=>處理(篩選契約,結算日)=>輸出datatable=>輸出datagridview(剩日期、契約、結算、成交時間、成交價格、成交量)=>於是我現在手上有一個dt物件是篩選好的tick資料型態,

    接著我使用您所貼的方法,new了一個tick物件,new了一個OHLCBar物件,
    想法上是將我原本的dt(tick)匯入tick物件,
    然後使用private IEnumerable generate_OHLCBar(IEnumerable ticks)方法

    得到OHLCBar物件,而我就可以在使用OHLCbar物件產生新的datagridview和畫K線,(以上是我的思考理解),但我卡關的地方在於,當我用for將dt載入對應的tick物件之後,我該怎麼使用這個方法呢?(方法的傳入和傳出部分有點看不懂,還請大大指示) 又,不曉得我上述的思考過程有誤嗎?

  2. 您好,剛剛試作了一下,感覺很棒。
    不過第15行Convert.ToInt32感覺好像會四捨五入,
    改成Math.Floor不知您的看法如何? 謝謝。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *