MongoDB:股票报价数据聚合

2 投票
1 回答
3079 浏览
提问于 2025-04-18 05:08

我正在使用MongoDB来存储我的股票交易数据。每分钟我会为每个股票符号存储一个文档:

{
"_id" : ObjectId("535fb330f6a03d59077db43c"),
"symbol" : "AAPL",
"ts_minute" : ISODate("2014-04-29T14:12:00Z"),
"ticks" : [
    {
        "mu" : 115864,
        "ae" : true,
        "t" : 2,
        "v" : 571.93
    },
    {
        "mu" : 803378,
        "ae" : true,
        "t" : 2,
        "v" : 571.91
    },
    {
        "mu" : 903378,
        "ae" : false,
        "t" : null,
        "v" : 9000
    }
}

这里的mu表示自ts_minute以来的微秒数,t是交易类型(比如买入、卖出、开盘、收盘、成交量等),而v是具体的数值。

为了把这些数据整理成每分钟的OHLC(开盘、最高、最低、收盘)数据,我使用了以下代码(用的是PyMongo):

query = {'$match': {'symbol': 'AAPL'}}
projection = {
    '$project': {
        'symbol':       1,
        'year':         {'$year':   '$ts_minute'},
        'month':        {'$month':  '$ts_minute'},
        'day':          {'$dayOfMonth': '$ts_minute'},
        'hour':         {'$hour':   '$ts_minute'},
        'minute':       {'$minute': '$ts_minute'},
        'ts_minute':    1,
        'ticks':        1
    }
}
unwind = {'$unwind': '$ticks'}
sort = {'$sort': {'ts_minute': 1}}
group = {
    '$group': {
        '_id': {
            'symbol':   '$symbol',
            'year':     '$year',
            'month':    '$month',
            'day':      '$day',
            'hour':     '$hour',
            'minute':   '$minute'
        },
        'open':     {'$first':  '$ticks.v'},
        'high':     {'$max':    '$ticks.v'},
        'low':      {'$min':    '$ticks.v'},
        'close':    {'$last':   '$ticks.v'},
    }
}
bars = tick_collection.aggregate([query, projection, unwind, sort, group])

问题是,我把成交量的交易数据和价格的交易数据存储在同一个数组里。成交量的交易数据是通过t等于null来识别的。所以当我进行分组时,价格数据和成交量数据就会混在一起。我想把数据整理成OHLCV,其中OHLC是基于t不等于null的部分,而V应该是数组中最后一个t等于null的元素。

这样说清楚了吗?还是说这只是个糟糕的设计?;-)

1 个回答

2

为了提高性能,你真的需要把 $sort 放在 unwind 之前。这样它会和 $match 一起处理,并且会使用合适的索引(希望你有 {symbol:1,ts_minute:1} 这个索引)。而 project 应该在 unwind 之后进行,这样才能创建你需要的价格和交易量字段,以便进行聚合。看起来你应该直接按 ts_minute 来分组。需要做的改动是:

query = {'$match': {'symbol': 'AAPL'}}
sort = {'$sort': {'ts_minute': 1}}
unwind = {'$unwind': '$ticks'}
projection = {
    '$project': {
        'symbol':       1,
        'ts_minute':    1,
        'volume' : { '$cond' : [
        {"$eq" : ["$ticks.t",null]},
        "$ticks.v",
        0
    ] },
    "price" : { "$cond" : [
        {"$eq" : ["$ticks.t",null] },
        null,
        "$ticks.v"
    ] }
    }
}

group = {
    '$group': {
        '_id': {
            'symbol':   '$symbol',
            'minute':   '$ts_minute'
        },
        'open':     {'$first':  '$price'},
        'high':     {'$max':    '$price'},
        'low':      {'$min':    '$price'},
        'close':    {'$last':   '$price'},
        'volume':   {'$sum':    '$volume'}
    }
}
bars = tick_collection.aggregate([query, sort, unwind, projection, group])

撰写回答