【问题标题】:Lodash groupby, step value, sum and sort together for order book grouping on array of objectsLodash groupby, step value, sum and sort together for order book grouping on the array of objects
【发布时间】:2020-05-10 17:17:44
【问题描述】:

我有一个如下的对象数组

var orders={
  '8549.5': { side: 'Sell', size: 380210 },
  '8549.0': { side: 'Sell', size: 51700 },
  '8548.5': { side: 'Sell', size: 14136 },
  '8547.5': { side: 'Sell', size: 1108 },
  '8547': { side: 'Sell', size: 84190 },
  '8544.5': { side: 'Sell', size: 65800 },
  '8543.5': { side: 'Sell', size: 24755 },
  '8542.5': { side: 'Sell', size: 6233 },
  '8541.5': { side: 'Sell', size: 10206 },
  '8540.5': { side: 'Sell', size: 228742 },
  '8539.5': { side: 'Buy', size: 4001 },
  '8538.5': { side: 'Buy', size: 50001 },
  '8537.5': { side: 'Buy', size: 75000 },
  '8536.5': { side: 'Buy', size: 77367 },
  '8535.5': { side: 'Buy', size: 75000 }}

我想实现以下目标:

  • 如果分组索引为 1 - 所有订单价格都需要按 1 分组(8549、8548、8547 等)。
  • 大小需要相加
  • 每边所有尺寸的总和

我尝试过常规循环方法,但它真的很慢,对象的大小约为 50000 到 250000 个键。

我目前通过分组 1 得到的最终结果是:

Sell: [{"price": 8549, "size": 431910, "total": 431910}, 
        {"price": 8548, "size": 14136, "total": 446046}, 
        {"price": 8547, "size": 85298, "total": 531344 } 
        ...

and similar for sell

有没有lodash的实现可以解决分组索引、sum和total?

这是当前的最小代码

var realTimelastPrice=null;
var lastPriceSide=null;
var bid=null; var ask=null;
var order={};
var orderBook={};
var orderSell={};
var orderBuy={};



function setBids(gapPrice)
{

   var lastPrice = this.realTimelastPrice

   var startBuyPrice = this.bid

   gapPrice=parseFloat(gapPrice)

   var showMaximum = 10

   var startBuyViewPrice = Math.floor(startBuyPrice/gapPrice)*gapPrice


   var tmpOrder = {
      price:null,
      size:null,
      total: null

   }
   var currentBuyViewPrice = startBuyViewPrice
   var totalBuy=0;
    var buys;
   var cntShow = 0


   for(var price = startBuyPrice;cntShow<=showMaximum;price -= 0.5)
   {

      if(this.orderBook[price]==undefined)
         continue
      var tmpSize = this.orderBook[price].size;

      if(currentBuyViewPrice>price)
      {
         currentBuyViewPrice -= gapPrice

         tmpOrder['size'] = new Intl.NumberFormat('en-US').format(tmpOrder['size'])
         tmpOrder['total'] = new Intl.NumberFormat('en-US').format(totalBuy)
         tmpOrder['price'] = new Intl.NumberFormat('en-US').format(tmpOrder['price'].toFixed(1))

         buys+='<tr><td style="color:white !important;">'+tmpOrder['total']+'</td><td style="color:white !important;">'+commaNumber(tmpOrder['size'])+'</td><td>'+tmpOrder['price']+'</td></tr>'

         tmpOrder = {
            price:null,
            size:null,
            total: null
         }
         cntShow += 1
      }
      totalBuy+=tmpSize;
      if(tmpOrder['price']==null)
      {
         tmpOrder['price'] = currentBuyViewPrice
         tmpOrder['size'] = tmpSize
      }
      else
      {
         tmpOrder['size'] += tmpSize    
      }
      if(showMaximum==cntShow) break

   }

         $(".orderbook-table-bids tbody").html(buys)

}


 function setAsks(gapPrice)
{
   var startSellPrice = this.ask
   gapPrice=parseFloat(gapPrice)
   var showMaximum = 12

   var startSellViewPrice = Math.ceil(startSellPrice/gapPrice)*gapPrice

   var tmpOrder = {
      price:null,
      size:null,
      total:null
   }

   var totalSell=0;
   var currentSellViewPrice = startSellViewPrice
   var sells;
   var cntShow = 0

   for(var price = startSellPrice;cntShow<=showMaximum;price += 0.5)
   {
      var tmpViewPrice = currentSellViewPrice
      if(this.orderBook[price]==undefined)
         continue
      var tmpSize = this.orderBook[price].size;

      if(currentSellViewPrice<price)
      {
         currentSellViewPrice += gapPrice

         tmpOrder['total'] = new Intl.NumberFormat('en-US').format(totalSell)
        tmpOrder['size'] = new Intl.NumberFormat('en-US').format(tmpOrder['size'])
         tmpOrder['price'] = new Intl.NumberFormat('en-US').format(tmpOrder['price'].toFixed(1))


            sells+='<tr><td >'+tmpOrder['price']+'</td><td style="color:white !important;">'+commaNumber(tmpOrder['size'])+'</td><td style="color:white !important;">'+tmpOrder['total']+'<td></tr>'

         tmpOrder = {
            price:null,
            size:null,
            total: null
         }
         cntShow += 1
      }
      totalSell += tmpSize
      if(tmpOrder['price']==null)
      {
         tmpOrder['price'] = currentSellViewPrice
         tmpOrder['size'] = tmpSize
      }
      else
      {
         tmpOrder['size'] += tmpSize    
      }


      if(showMaximum==cntShow) break

   }
         $(".orderbook-table-asks tbody").html(sells)
}

async function appendOrderbooks(res)
{
   var data = res.data
   var action = res.action

   if(action==='partial')
   {
      data.forEach(async ($element) => {

         if($element.side == 'Sell' && realTimelastPrice <= $element.price && ask > $element.price)
            {
                ask = $element.price
            }

            if($element.side == 'Buy' && realTimelastPrice >= $element.price && bid < $element.price)
            {
                bid = $element.price
         }

         order[$element.id] = $element
         orderBook[$element.price] = {
            side:$element.side,
            size:$element.size
         }

      })
   }


   if(action==='insert')
   {
      data.forEach(async ($element) => {
         if($element.side == 'Sell' && realTimelastPrice <= $element.price && ask > $element.price)
            {
                ask = $element.price
            }

            if($element.side == 'Buy' && realTimelastPrice >= $element.price && bid < $element.price)
            {
               bid = $element.price
            }
         order[$element.id] = $element
         orderBook[$element.price] = {
            side:$element.side,
            size:$element.size
         }

      })
   }
   if(action==='update')
   {
      data.forEach(async ($element) => {
         if($element.side == 'Sell' && realTimelastPrice <= $element.price && ask > $element.price)
            {
                ask = $element.price
            }

            if($element.side == 'Buy' && realTimelastPrice >= $element.price && bid < $element.price)
            {
                bid = $element.price
            }

         var tmpPrice = order[$element.id].price
            orderBook[tmpPrice] = {side:$element.side,size: $element.size}

      })

   }
   if(action==='delete')
   {
      data.forEach(async ($element) => {

         delete order[$element.id]
      })
   }
   var gapPrice =  1
   var debounceCalculate = _.debounce(setBids, 0, false);
   debounceCalculate(gapPrice);
    debounceCalculate = _.debounce(setAsks, 0, false);
   debounceCalculate(gapPrice);
}


var ws = new WebSocket("wss://www.bitmex.com/realtime?");

ws.onmessage = function(e) {
if(e.table=="orderBookL2")
{
appendOrderbooks(e)
}
if(e.table=="instrument")
{

    if('lastPrice' in e.data[0])
    realTimelastPrice=data[0].lastPrice

    if('bidPrice' in e.data[0])
{
   bid=data[0].bidPrice;
}
if('askPrice' in e.data[0])
{
ask=data[0].askPrice;
}

}
}

基本上是 bitmex 订单簿上可用的分组功能。

【问题讨论】:

  • 你的代码有什么问题?
  • 我的代码很慢 ...什么代码?您没有显示任何内容供任何人进行故障排除。建议您花几分钟时间阅读How to Askminimal reproducible example
  • 我已经更新了原问题中的代码。

标签: javascript node.js grouping lodash


【解决方案1】:

您可以从数据中获取条目,按键的整数值分组并创建具有所需边的对象的对象。

第二部分使用临时对象,获取键,对它们进行降序排序,并将新条目映射到所需的总数。最后创建一个新对象。

var orders = { 8549.5: { side: 'Sell', size: 380210 }, 8549.0: { side: 'Sell', size: 51700 }, 8548.5: { side: 'Sell', size: 14136 }, 8547.5: { side: 'Sell', size: 1108 }, 8547: { side: 'Sell', size: 84190 }, 8544.5: { side: 'Sell', size: 65800 }, 8543.5: { side: 'Sell', size: 24755 }, 8542.5: { side: 'Sell', size: 6233 }, 8541.5: { side: 'Sell', size: 10206 }, 8540.5: { side: 'Sell', size: 228742 }, 8539.5: { side: 'Buy', size: 4001 }, 8538.5: { side: 'Buy', size: 50001 }, 8537.5: { side: 'Buy', size: 75000 }, 8536.5: { side: 'Buy', size: 77367 }, 8535.5: { side: 'Buy', size: 75000 } },
    temp = Object
        .entries(orders)
        .reduce((r, [k, { side, size }]) => {
            const price = Math.floor(k);
            r[side][price] = r[side][price] || { price, size: 0 };
            r[side][price].size += size;
            return r;
        }, { Sell: {}, Buy: {} }),
    result = Object.fromEntries(
        Object
            .entries(temp)
            .map(([k, o]) => [
                k,
                Object
                    .keys(o)
                    .sort((a, b) => b - a)
                    .map((total => k => ({ ...o[k], total: total += o[k].size }))(0))
            ])
    );


console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 如何将结果限制为 10 或 20,并将分组索引更改为 5、10 或 15?我正在创建订单簿的分组,如下所示:bitmex.com/app/trade/XBTUSD
  • 您可以选择想要的范围并将价格更改为const price = Math.floor(k / range) * range;
  • 订单中有 30000 个键,这个解决方案变得非常慢。有没有办法限制地图只做 20 个结果?
  • 如果我知道,20 个结果是什么意思?
  • 排序后,我只需要显示 20 个买入和 20 个卖出结果。因为它尝试排序和映射 30000 个键,所以它变得很慢。
猜你喜欢
  • 2022-11-14
  • 2022-12-28
  • 1970-01-01
  • 2022-11-20
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 2022-12-01
  • 2010-09-28
相关资源
最近更新 更多