ping_pong/events/orders_order_fill.js

'use strict'

const { Order } = require('bfx-api-node-models')
const genCID = require('../../util/gen_client_id')
const { Config } = require('bfx-api-node-core')
const { DUST } = Config

/**
 * Triggered on atomic order fill. If it was a `ping`, the associated `pong`
 * is submitted. Otherwise it if was a `pong` and the instance was configured
 * as `endless`, the associated `ping` is submitted. If not `endless`, nothing
 * is done.
 *
 * @memberOf module:PingPong
 * @listens AOHost~ordersOrderFill
 *
 * @param {AOInstance} instance - AO instance
 * @param {object} order - the order that filled
 * @returns {Promise} p - resolves on completion
 */
const onOrdersOrderFill = async (instance = {}, order) => {
  const { state = {}, h = {} } = instance
  const { args = {}, gid, pingPongTable, activePongs } = state
  const { emit, debug, updateState } = h
  const { price } = order
  const {
    endless, submitDelay, symbol, pingAmount, pongAmount, hidden, lev,
    _margin, _futures
  } = args

  if (order.amount > DUST) { // not fully filled
    return
  }

  const sharedOrderParams = {
    meta: { _HF: 1 },
    symbol,
    hidden,
    gid
  }

  if (_futures) {
    sharedOrderParams.lev = lev
  }

  const {
    [price]: pongPrice,
    ...nextPingPongTable
  } = pingPongTable

  if (!pongPrice) {
    const {
      [price]: pingPrice,
      ...nextActivePongs
    } = activePongs

    if (pingPrice) {
      debug('pong filled: %f', price)

      // NOTE: Shadows from above
      const nextPingPongTable = !endless
        ? pingPongTable
        : {
          ...pingPongTable,
          [pingPrice]: price
        }

      await updateState(instance, {
        activePongs: nextActivePongs,
        pingPongTable: nextPingPongTable
      })

      if (endless) {
        const pingOrder = new Order({
          ...sharedOrderParams,

          price: pingPrice,
          cid: genCID(),
          type: _margin || _futures ? Order.type.LIMIT : Order.type.EXCHANGE_LIMIT,
          amount: pingAmount
        })

        await emit('exec:order:submit:all', gid, [pingOrder], submitDelay)
      } else if (
        Object.keys(pingPongTable).length === 0 &&
        Object.keys(nextActivePongs).length === 0
      ) {
        debug('all orders filled')
        await emit('exec:stop')
      }
    }

    return
  }

  const pongOrder = new Order({
    ...sharedOrderParams,

    price: pongPrice,
    cid: genCID(),
    type: _margin || _futures ? Order.type.LIMIT : Order.type.EXCHANGE_LIMIT,
    amount: -pongAmount
  })

  debug('submitting pong order %f for ping %f', pongPrice, price)

  await emit('exec:order:submit:all', gid, [pongOrder], submitDelay)
  await updateState(instance, {
    pingPongTable: nextPingPongTable,
    activePongs: {
      ...activePongs,
      [pongPrice]: price
    }
  })
}

module.exports = onOrdersOrderFill