简体   繁体   中英

How to get the sum of the volume of highest and lowest priced items in linq

The actual query I am trying to write is slightly trickier than the title suggests. I have a list of orders like such: List<Order> , an order looks like this:

public class Order
{
    private StockCodes _stockCode;
    private bool _bidSide;
    private int _volume;
    private decimal _price;
}

I need to publish the best bid price and volume and the best sell price and volume given a specific stock code. The best bid price is defined as the HIGHEST price where bidSide is true. The best sell price is defined as the LOWEST price where bidSide is false.

For example given the following data for stock code "ABC":

 { bidSide: true, volume: 25, price: 25  }
 { bidSide: true, volume: 25, price: 25  }
 { bidSide: true, volume: 25, price: 5  }

 { bidSide: false, volume: 100, price: 1  }
 { bidSide: false, volume: 50, price: 2}
 { bidSide: false, volume: 75, price: 8 }

Best bid: price 25, volume 50 (since there are 2 orders at the highest price) Best sell: price 1, volume 100 (since there is just 1 order at the lowest price)

Lastly, I need to account for when there are no bid or sell orders. Efficiency is of high priority, so If I am able to do this in one linq statement that would be preferred.

To do this efficiently, you really only want to iterate over the data once. Unfortunately, that makes it a real pain to implement with LINQ, as there's quite a lot of work to do.

Personally I would suggest that you don't do this with LINQ - you could implement it with Aggregate , but it wouldn't be terribly pleasant. It's not too bad with a simple foreach loop though. Something like:

int buyVolume = -1;
int sellVolume = -1;
decimal buyPrice = decimal.MinValue;
decimal sellPrice = decimal.MaxValue;

foreach (var order in orders)
{
    if (order.bidSide)
    {
        if (order.Price > buyPrice)
        {
            buyPrice = order.Price;
            buyVolume = order.Volume;
        }
        else if (order.Price == buyPrice)
        {
            buyVolume += order.Volume;
        }
    }
    else
    {
        if (order.Price < sellPrice)
        {
            sellPrice = order.Price;
            sellVolume = order.Volume;
        }
        else if (order.Price == sellPrice)
        {
            sellVolume += order.Volume;
        }
    }
}

// Check sellVolume == -1 to verify whether we've seen any sale orders
// Check buyVolume == -1 to verify whether we've seen any buy orders
// Use buyPrice/buyVolume and sellPrice/sellVolume otherwise

Doing it as efficiently as possible in LINQ would effectively mean putting all that logic in the loop into a function to pass into Aggregate - and you'd probably want to create a custom value type to hold the four values, to avoid creating more objects than you need to. That may be overkill, but you did say you wanted it as efficient as possible...

HIGHEST = orders.Max(x => x.bidSide ? x.price : (decimal?)null) ?? 0M

Similar for LOWEST.

Linq2SQL will not convert this into an efficient query, unfortunately. It will execute in one single query, but the data will be scanned once per Max operation (in your case two times: HIGHEST and LOWEST). In raw SQL you can do this in one pass over the data.

This could do LINQ wise...

var bids = (from o in orders
                where o.StockCode == "ABC" && o.BidSide == true
                group o by o.Price)
                .OrderByDescending(g => g.Key)
                .FirstOrDefault();
var bidVolume = bids != null ? new Order { Price = bids.Key, Volume = bids.Sum(g => g.Volume) } : null;

var sells = (from o in orders
                where o.StockCode == "ABC" && o.BidSide == false
                group o by o.Price)
                .OrderBy(g => g.Key)
                .FirstOrDefault();
var sellVolume = sells != null ? new Order { Price = sells.Key, Volume = sells.Sum(g => g.Volume) } : null;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM