Search code examples
mongodbmongodb-queryaggregation-frameworkmongodb-aggregation

MongoDB Aggregation pipeline problems


I'm new to mongoDB and am having difficulty getting my head around aggregation pipelines.

I have created a database that holds information regarding my stock trading. In a cut down version one document from my portfolio collection looks a bit like this

   {
   "date" : 2015-12-31T15:50:00.000Z,
   "time" : 1550,
   "aum"  : 1000000,
   "basket" :[
   {
     "_id" : "Microsoft",
     "shares" : 10,
     "price"  : 56.53,
     "fx"     : 1.0
   },
   .
   .
   .
   {
     "_id" : "GOOG.N",
     "shares" : 20,
     "price"  : 759.69,
     "fx"     : 1.0
   }

]

So, for each day, I keep track of my assets under management (aum) and a list of all the positions I hold with the current price. What I need to do is to calculate the daily net and gross exposure for the portfolio as a percentage of aum. Net exposure is simply:

sum(shares*price*fx)/aum

over all the stocks. Gross exposure is:

abs(shares*price*fx)/aum

(a negative position means a short position). I need to do this as a single query using the aggregation framework. I have tried numbers of queries but none seem to work so clearly I'm just wandering around in the dark. Can anyone give some guidance?

My query looks like this

db.strategy.aggregate(

  // Pipeline
  [
    // Stage 1
    {
      $project: {
        "_id": 0,
        "date":1,
        "time":1,
        "aum":1,
        "strategyName":1,
        "gExposure": {$divide: ["$grossExposure","$aum"]}
      }
    },

    // Stage 2
    {
      $group: {
        _id :{ date:"$date",time:"$time",strategyName:"$strategyName"},
        grossExposure: { $sum: { $abs: {$multiply: [ "$basket.sysCurShares","$basket.price","$basket.fx" ] } }}
      }
    },

    // Stage 3
    {
      $sort: {
      "_id.date": 1,  "_id.time": 1,  "_id.strategyName": 1 
      }
    }

  ]
);

The query runs but the calculated value is zero. My projection isn't working as I'd expect either as I would like all the data flattened to a two dimensional table.


Solution

  • Since the basket field is an array, you need to flatten it using $unwind before running the $group aggregate operation. Also, create a new field in the $project that holds the exposure before the $group pipeline. Continuing from your previous attempt, you could try the following pipeline:

    db.strategy.aggregate([
        { "$unwind": "$basket" },
        {
            "$project": {
                "date": 1,
                "time": 1,          
                "strategyName": 1,
                "exposure": { 
                    "$multiply": ["$basket.sysCurShares", "$basket.price", "$basket.fx"] 
                }
            }
        },    
        {
            "$group": {
                "_id": { 
                    "date": "$date",
                    "time": "$time",
                    "strategyName": "$strategyName"
                },
                "totalExposure": { "$sum": "$exposure" },
                "aum": { "$first": "$aum" }
            }
        },
        {
            "$project": {
                "_id": 0,
                "date": "$_id.date",
                "time": "$_id.time",         
                "strategyName": "$_id.strategyName",
                "netExposure": { "$divide": ["$totalExposure", "$aum"] },
                "grossExposure": {
                    "$abs": { "$divide": ["$totalExposure", "$aum"] } 
                }
            }
        }, 
        { "$sort": { "date": 1,  "time": 1,  "strategyName": 1 } }
    ]);