如何使用 mongodb 聚合 OHLC 条?
How to aggregate OHLC bars with mongodb?
我在导入 Mongo 数据库的一些市场数据上有 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 的聚合函数,但我不知所措,我应该如何处理这个问题以及我应该如何对管道操作进行排序。
我是否先按 'Date' 分组,然后按 'Time' 排序,然后再按 $first、$last、$max 和 $min 分组?
或者我是否首先创建一个新字段以某种方式组合 'Date' 和 'Time' 然后继续分组?
虽然我不需要首先以某种方式将 "Date" 和 "Time" 字段从字符串转换为日期字段,以便 Mongo 知道如何正确排序和匹配? ...但是我应该按照哪个顺序来做呢?
我仍然是 MongoDB 的新手,所以任何建议都将不胜感激。
我想,你可以简单地使用 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;
不幸的是,如果您打算多次生成此图,则可以添加一个字段时间(包含时间戳)!
您需要同时按日期和时间(准确地说是时间间隔)进行分组。看看 https://docs.mongodb.com/ecosystem/use-cases/storing-log-data/#counting-requests-by-day-and-page for examples of the pipeline and Whosebug itself - the similar questions were answered many times, e.g. Group result by 15 minutes time interval in MongoDb
如果您需要快速 生成X-bar 间隔并且数据集足够大以致聚合速度明显变慢,您可能需要pre-aggregate 数据。该模式在 https://www.mongodb.com/blog/post/schema-design-for-time-series-data-in-mongodb and https://docs.mongodb.com/ecosystem/use-cases/pre-aggregated-reports-mmapv1/ 中有详细描述(如果您使用的是 WiredTiger 引擎,请忽略 pre-allocation 部分)
好的,我想出了一个解决方案:
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})
以下是每个管道阶段的解释:
- 项目
使用 'dateFromString' 将 'Date' 和 'Time' 组合成一个 ISODate 对象('dts' - 代表日期时间戳)。保留其他 OHLC 字段。
- 匹配
根据日期过滤掉 运行ge
- 排序
按新的 ISODate 对象排序 ('dts')。
- 组
将具有相同年、月、日、小时和 5 分钟间隔的所有这些文档组合在一起。分钟间隔使用公式:
minute = minuteIn - (minuteIn % i) + i,其中 i = 分钟间隔。我正在添加 'i',以便将第 00、01、02、03 和 04 分钟聚合到下一个 05 分钟间隔(而不是之前的 00 分钟间隔)。注意:如果您想要 1 小时、4 小时、每日柱等...那么您需要相应地调整 _id 部分。
注意:
我在这里使用 {allowDiskUse: true} 因为有一次我 运行 在排序阶段进入内存限制。
也许有人可以想出更简单的方法来做到这一点?
更新:
正如我在上面的 4) 中指出的那样,我提到我将 "i"(分钟间隔)添加到结果分钟。但是,当我这样做时,我最终在输出中显示了一个“60”分钟的间隔。您应该只有 0、5、10、15、...55 分钟柱,不应有 60 分钟柱。所以这是不正确的。
此外,如果您与交易平台(即 Thinkorswim)进行比较,您会发现标准做法是使用前 5 分钟间隔作为柱的时间戳。例如,5 分钟柱 9:25 表示这些分钟柱的聚合:9:25、9:26、9:27、9:28、9:29 .
我在导入 Mongo 数据库的一些市场数据上有 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 的聚合函数,但我不知所措,我应该如何处理这个问题以及我应该如何对管道操作进行排序。
我是否先按 'Date' 分组,然后按 'Time' 排序,然后再按 $first、$last、$max 和 $min 分组?
或者我是否首先创建一个新字段以某种方式组合 'Date' 和 'Time' 然后继续分组?
虽然我不需要首先以某种方式将 "Date" 和 "Time" 字段从字符串转换为日期字段,以便 Mongo 知道如何正确排序和匹配? ...但是我应该按照哪个顺序来做呢?
我仍然是 MongoDB 的新手,所以任何建议都将不胜感激。
我想,你可以简单地使用 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;
不幸的是,如果您打算多次生成此图,则可以添加一个字段时间(包含时间戳)!
您需要同时按日期和时间(准确地说是时间间隔)进行分组。看看 https://docs.mongodb.com/ecosystem/use-cases/storing-log-data/#counting-requests-by-day-and-page for examples of the pipeline and Whosebug itself - the similar questions were answered many times, e.g. Group result by 15 minutes time interval in MongoDb
如果您需要快速 生成X-bar 间隔并且数据集足够大以致聚合速度明显变慢,您可能需要pre-aggregate 数据。该模式在 https://www.mongodb.com/blog/post/schema-design-for-time-series-data-in-mongodb and https://docs.mongodb.com/ecosystem/use-cases/pre-aggregated-reports-mmapv1/ 中有详细描述(如果您使用的是 WiredTiger 引擎,请忽略 pre-allocation 部分)
好的,我想出了一个解决方案:
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})
以下是每个管道阶段的解释:
- 项目
使用 'dateFromString' 将 'Date' 和 'Time' 组合成一个 ISODate 对象('dts' - 代表日期时间戳)。保留其他 OHLC 字段。
- 匹配
根据日期过滤掉 运行ge
- 排序
按新的 ISODate 对象排序 ('dts')。
- 组
将具有相同年、月、日、小时和 5 分钟间隔的所有这些文档组合在一起。分钟间隔使用公式: minute = minuteIn - (minuteIn % i) + i,其中 i = 分钟间隔。我正在添加 'i',以便将第 00、01、02、03 和 04 分钟聚合到下一个 05 分钟间隔(而不是之前的 00 分钟间隔)。注意:如果您想要 1 小时、4 小时、每日柱等...那么您需要相应地调整 _id 部分。
注意: 我在这里使用 {allowDiskUse: true} 因为有一次我 运行 在排序阶段进入内存限制。
也许有人可以想出更简单的方法来做到这一点?
更新:
正如我在上面的 4) 中指出的那样,我提到我将 "i"(分钟间隔)添加到结果分钟。但是,当我这样做时,我最终在输出中显示了一个“60”分钟的间隔。您应该只有 0、5、10、15、...55 分钟柱,不应有 60 分钟柱。所以这是不正确的。
此外,如果您与交易平台(即 Thinkorswim)进行比较,您会发现标准做法是使用前 5 分钟间隔作为柱的时间戳。例如,5 分钟柱 9:25 表示这些分钟柱的聚合:9:25、9:26、9:27、9:28、9:29 .