如何用mongodb聚合OHLC条?

时间:2018-04-03 08:01:31

标签: mongodb financial

我在Mongo DB中导入的一些市场数据上有1分钟OHLC条。

每个文档都是这样的:

{
    "_id" : ObjectId("5ac3163f31a0632c7642ca1c"),
    "Date" : "08/06/2007",
    "Time" : "15:01",
    "Open" : 1310,
    "High" : 1310.25,
    "Low" : 1309.5,
    "Close" : 1310,
    "Up" : 209,
    "Down" : 165,
    "Volume" : 0
}

我想构建一个允许我从这些数据中快速生成X-bar间隔的函数。即生成输出5分钟柱,1小时柱,每日柱等...我还希望能够过滤掉数据范围。

我一直在玩Mongo的聚合函数,但是我不知道如何处理这个以及我应该如何订购管道操作。

我是先按'日期'分组,然后按'时间'排序,再按$ first,$ last,$ max和$ min分组?

或者我首先以某种方式创建一个新的字段,将“日期”和“时间”结合起来然后进行分组?

虽然我不需要首先以某种方式将“Date”和“Time”字段从字符串转换为Date字段,以便Mongo知道如何正确排序和匹配? ......但是那时我会做哪个订单呢?

我仍然是MongoDB的新手,所以任何建议都会受到赞赏。

3 个答案:

答案 0 :(得分:1)

好的,我想出了一个解决方案:

db.minbars.aggregate([
   {
      $project: 
      {
         dts: 
         {
            $dateFromString: 
            {
               dateString: 
               {
                  $concat: ['$Date', '$Time']
               }
            }
         },
         Open:1, 
         High:1, 
         Low:1, 
         Close:1
      }   
   },
   {
      $match: 
      {
         dts: 
         { 
            $gte: ISODate("2016-01-01T00:00:00.000Z"), 
            $lte: ISODate("2016-12-31T00:00:00.000Z")
         }
      }
   },
   {
      $sort: { dts : 1 }
   },
   {
      $group:
      {
         _id: 
         {
            year: {$year: "$dts"},
            month: {$month: "$dts"},
            day: {$dayOfMonth: "$dts"},               
            hour: {$hour: "$dts"},
            min: 
            {
                $add: 
                [
                   {$subtract:
                   [
                      {$minute: "$dts"},
                      {$mod: [{$minute: "$dts"}, 5]}
                   ]},
                   5   
                ]
            }   
         },
         Open: {$first: "$Open"},
         High: {$max: "$High"},
         Low: {$min: "$Low"},
         Close: {$last: "$Close"}
      }
   } 
], {allowDiskUse: true})

以下是每个管道阶段的解释:

  1. 项目
  2. 使用'dateFromString'将'Date'和'Time'组合成一个ISODate对象('dts' - 代表日期时间戳)。保留其他OHLC字段。

    1. 匹配
    2. 根据日期范围过滤掉

      1. 排序
      2. 按新的ISODate对象排序('dts')。

        1. 将具有相同年,月,日,小时和5分钟间隔的所有文档组合在一起。分钟间隔使用公式: minute = minuteIn - (minuteIn%i)+ i,其中i =分钟间隔。我正在添加'i',以便将分钟00,01,02,03和04聚合到下一个05分钟的间隔(而不是之前的00分钟间隔)。注意:如果你想要1小时,4小时,每日酒吧等等,那么你需要相应地调整_id部分。

          注意: 我在这里使用{allowDiskUse:true}因为我曾经在Sort阶段遇到了Memory约束。

          也许有人可以想出一个更简单的方法来做到这一点?

          更新:

          正如我在上面4)中所提到的,我提到我在结果分钟中添加了“i”(分钟间隔)。然而,当我这样做时,我最终在输出中出现了'60'分钟的间隔。你应该只有0,5,10,15,... 55分钟的酒吧,不应该有60分钟的酒吧。所以这不正确。

          此外,如果您与交易平台(即Thinkorswim)进行比较,您可以看到标准做法是使用前5分钟间隔作为条形图的时间戳。例如,5分钟栏9:25表示这些分钟栏的聚合:9:25,9:26,9:27,9:28,9:29。

答案 1 :(得分:0)

我认为,你可以简单地使用PHP。 要降低代码的复杂性,请不要创建包含日期和时间的新字段。

    $outputs = array();
    $raw_datas = array();
    foreach($raw_datas as $data){
      $date = \DateTime::createFromFormat('D/M/Y H:i', $data["Date"]." ".$data["Time"]);
      $outputs['daily'][$date->format("D/M/Y")][] = $date; //or $date.id if you aim to use AJAX later
      $outputs['hourly'][$date->format("D/M/Y H")][] = $date; //or $date.id if you aim to use AJAX later
//      And so on...
//      ....
    }

    return $outputs;

不幸的是,如果你想多次生成这个图,你可以添加一个字段时间(包含时间戳)!

答案 2 :(得分:0)

您需要按日期和时间进行分组(准确的时间间隔)。请查看https://docs.mongodb.com/ecosystem/use-cases/storing-log-data/#counting-requests-by-day-and-page以获取管道和StackOverflow本身的示例 - 类似的问题已经多次回答,例如Group result by 15 minutes time interval in MongoDb

如果您需要快速生成X-bar间隔,并且数据集足够大,聚合速度明显变慢,则可能需要预先聚合数据。该模式在https://www.mongodb.com/blog/post/schema-design-for-time-series-data-in-mongodbhttps://docs.mongodb.com/ecosystem/use-cases/pre-aggregated-reports-mmapv1/中有详细描述(如果您使用的是WiredTiger引擎,则忽略预分配部分)