execute = async (logger, rawData) => {
  const data = rawData;

  const {
    action,
    symbol,
    symbolInfo: { baseAsset },
    symbolConfiguration: {
      buy: { currentGridTradeIndex: currentBuyGridTradeIndex },
      sell: { currentGridTradeIndex: currentSellGridTradeIndex }
    }
  } = data;

  const humanisedBuyGridTradeIndex = currentBuyGridTradeIndex + 1;
  const humanisedSellGridTradeIndex = currentSellGridTradeIndex + 1;

  if (action !== 'not-determined') {
    logger.info(
      { action },
      'Action is already defined, do not try to determine action.'
    );
    return data;
  }

  // Check buy signal -
  //  if last buy price is less than 0
  //    and current price is less or equal than lowest price
  //    and current balance has not enough value to sell,
  //    and current price is lower than the restriction price
  //  then buy.
  if (canBuy(data)) {
    if (
      _.isEmpty(await getGridTradeLastOrder(logger, symbol, 'buy')) === false
    ) {
      return setBuyActionAndMessage(
        logger,
        data,
        'buy-order-wait',
        `There is a last gird trade buy order. Wait.`
      );
    }

    if (hasBalanceToSell(data)) {
      return setBuyActionAndMessage(
        logger,
        data,
        'wait',
        `The current price reached the trigger price. ` +
          `But you have enough ${baseAsset} to sell. ` +
          `Set the last buy price to start selling. ` +
          `Do not process buy.`
      );
    }

    const checkDisable = await isActionDisabled(symbol);
    logger.info(
      { tag: 'check-disable', checkDisable },
      'Checked whether symbol is disabled or not.'
    );
    if (checkDisable.isDisabled) {
      return setBuyActionAndMessage(
        logger,
        data,
        'buy-temporary-disabled',
        'The current price reached the trigger price. ' +
          `However, the action is temporarily disabled by ${checkDisable.disabledBy}. ` +
          `Resume buy process after ${checkDisable.ttl}s.`
      );
    }

    if (isGreaterThanTheATHRestrictionPrice(data)) {
      return setBuyActionAndMessage(
        logger,
        data,
        'wait',
        `The current price has reached the lowest price; however, it is restricted to buy the coin ` +
          `because ATH price higher than the current price.`
      );
    }

    if (await isExceedingMaxBuyOpenOrders(logger, data)) {
      return setBuyActionAndMessage(
        logger,
        data,
        'wait',
        `The current price has reached the lowest price; however, it is restricted to buy the coin ` +
          `because of reached maximum buy open orders.`
      );
    }

    if (await isExceedingMaxOpenTrades(logger, data)) {
      return setBuyActionAndMessage(
        logger,
        data,
        'wait',
        `The current price has reached the lowest price; however, it is restricted to buy the coin ` +
          `because of reached maximum open trades.`
      );
    }

    return setBuyActionAndMessage(
      logger,
      data,
      'buy',
      `The current price reached the trigger price for the grid trade #${humanisedBuyGridTradeIndex}. Let's buy it.`
    );
  }

  // Check sell signal - if
  //  last buy price has a value
  //  and total balance is enough to sell
  if (canSell(data)) {
    if (
      _.isEmpty(await getGridTradeLastOrder(logger, symbol, 'sell')) === false
    ) {
      return setSellActionAndMessage(
        logger,
        data,
        'sell-order-wait',
        `There is a last gird trade sell order. Wait.`
      );
    }

    // If tradingView recommendation is sell or strong sell
    const { shouldForceSell, forceSellMessage } = shouldForceSellByTradingView(
      logger,
      data
    );
    if (shouldForceSell) {
      // Prevent disable by stop-loss
      data.canDisable = false;

      // Notify as it's important message for now.
      // Eventually, should convert to logging to reduce unnecessary notifications.
      slack.sendMessage(
        `*${symbol}* Action - *Force sell*: \n- Message: ${forceSellMessage}`,
        { symbol, apiLimit: getAPILimit(logger) }
      );

      // Then sell market order
      return setSellActionAndMessage(
        logger,
        data,
        'sell-stop-loss',
        forceSellMessage
      );
    }

    // If current price is higher or equal than trigger price
    if (isHigherThanSellTriggerPrice(data)) {
      const checkDisable = await isActionDisabled(symbol);
      logger.info(
        { tag: 'check-disable', checkDisable },
        'Checked whether symbol is disabled or not.'
      );
      if (checkDisable.isDisabled) {
        return setSellActionAndMessage(
          logger,
          data,
          'sell-temporary-disabled',
          'The current price is reached the sell trigger price. ' +
            `However, the action is temporarily disabled by ${checkDisable.disabledBy}. ` +
            `Resume sell process after ${checkDisable.ttl}s.`
        );
      }

      // Then sell
      return setSellActionAndMessage(
        logger,
        data,
        'sell',
        "The current price is more than the trigger price. Let's sell."
      );
    }

    if (isLowerThanStopLossTriggerPrice(data)) {
      const checkDisable = await isActionDisabled(symbol);
      logger.info(
        { tag: 'check-disable', checkDisable },
        'Checked whether symbol is disabled or not.'
      );
      if (checkDisable.isDisabled) {
        return setSellActionAndMessage(
          logger,
          data,
          'sell-temporary-disabled',
          'The current price is reached the stop-loss price. ' +
            `However, the action is temporarily disabled by ${checkDisable.disabledBy}. ` +
            `Resume sell process after ${checkDisable.ttl}s.`
        );
      }

      // Then sell market order
      return setSellActionAndMessage(
        logger,
        data,
        'sell-stop-loss',
        'The current price is reached the stop-loss price. Place market sell order.'
      );
    }

    // otherwise, wait
    return setSellActionAndMessage(
      logger,
      data,
      'sell-wait',
      `The current price is lower than the selling trigger price ` +
        `for the grid trade #${humanisedSellGridTradeIndex}. Wait.`
    );
  }

  // If cannot buy/sell, then just return data
  return data;
}
Determine action for trade

@param {*} logger
@param {*} rawData