Forum Discussion

hnsbhat's avatar
hnsbhat
Helper I
8 years ago
Solved

Power Query - Aggregating OHLC data

Hello, I have below sample stock data for 15 min time interval for multiple dates which has open, high, low, close and volume. I need to convert 15 mins data into data for other time frames like 30 m...
  • MarcelBeug's avatar
    8 years ago

    My suggestion would be to create interval numbers, e.g. with aggregation 30 minutes, 9:15 and 9:30 are interval 0, 9:45 and 10:00 are interval 1, etcetera.

     

    Query Intervals creates a list with possible durations from which you can choose the value for parameter Interval, e.g. a list varying from 15 minutes to 2 hours:

     

    = List.Durations(#duration(0,0,15,0),8,#duration(0,0,15,0))

     

    Parameter Interval

     

     

    Query TimeIntervals gives a table with times and interval numbers for all quarters 9:15 through 15:30:

     

    let
        Source = #table(type table[Time = time],List.Zip({List.Times(#time(9,15,0),26,#duration(0,0,15,0))})),
        #"Added Custom" = Table.AddColumn(Source, "Interval", each Number.RoundDown(([Time] - #time(9,15,0))/Interval,0), Int64.Type)
    in
        #"Added Custom"

     

    Query IntervalStartTimes gives a table with the start times of each interval:

     

    let
        Source = TimeIntervals,
        #"Grouped Rows" = Table.Group(Source, {"Interval"}, {{"StartTime", each List.Min([Time]), type time}})
    in
        #"Grouped Rows"

     

    Query AggregatedDate gives the final result. Notice I grouped on Date, StartTime and Ticker (you didn't mention what to do with Ticker).

     

    let
        Source = Data,
        #"Added Index" = Table.AddIndexColumn(Source, "OriginalSort", 0, 1),
        #"Merged Queries" = Table.NestedJoin(#"Added Index",{"Time"},TimeIntervals,{"Time"},"TimeIntervals",JoinKind.LeftOuter),
        #"Expanded TimeIntervals" = Table.ExpandTableColumn(#"Merged Queries", "TimeIntervals", {"Interval"}, {"Interval"}),
        #"Merged Queries1" = Table.NestedJoin(#"Expanded TimeIntervals",{"Interval"},IntervalStartTimes,{"Interval"},"IntervalStartTimes",JoinKind.LeftOuter),
        #"Expanded IntervalStartTimes" = Table.ExpandTableColumn(#"Merged Queries1", "IntervalStartTimes", {"StartTime"}, {"StartTime"}),
        #"Sorted Rows" = Table.Buffer(Table.Sort(#"Expanded IntervalStartTimes",{{"OriginalSort", Order.Ascending}})),
        #"Grouped Rows" = Table.Group(#"Sorted Rows", {"Date", "StartTime", "Ticker"}, 
                            {{"OPEN", each List.First([OPEN]), type number},
                             {"HIGH", each List.Max([HIGH]), type number},
                             {"LOW", each List.Min([LOW]), type number},
                             {"CLOSE", each List.Last([CLOSE]), type number},
                             {"VOLUME", each List.Sum([VOLUME]), type number}}),
        #"Merged Columns" = Table.CombineColumns(#"Grouped Rows", {"Date", "StartTime"}, each _{0} & _{1},"Date"),
        #"Changed Type" = Table.TransformColumnTypes(#"Merged Columns",{{"Date", type datetime}})
    in
        #"Changed Type"

     

  • MarcelBeug's avatar
    MarcelBeug
    8 years ago

    My suggestion is to substract 9:30 instead of 9:15 and use List.Max to prevent negative values.

     

    let
        Source = #table(type table[Time = time],List.Zip({List.Times(#time(9,15,0),26,#duration(0,0,15,0))})),
        #"Added Custom" = Table.AddColumn(Source, "Interval", each List.Max({0,Number.RoundDown(([Time] - #time(9,30,0))/Interval,0)}), Int64.Type)
    in
        #"Added Custom"