EVE-Tools/node-43

View on GitHub
lib/messagePipeline/orderProcessor.js

Summary

Maintainability
A
3 hrs
Test Coverage
var postgres = require('../postgres');

// Load configuration
var config = require('../../config');

function getStatisticalInformation(regionID, typeID, callback) {
  //
  // Return statistical properties of given region/type pair
  //

  // Get connection from pool
  postgres(function(err, pgClient, done) {
    if (err) {
        // Return connection to pool
        done();

        err.module = 'pgConnect';

        // Handle errors
        return callback(err, null);
    } else {

      // Execute query
      pgClient.query({
        name: 'upsert_orders_stddev',
        text: 'SELECT COUNT(id), STDDEV(price), AVG(price) FROM market_data_orders WHERE invtype_id=$1 AND mapregion_id=$2 AND is_active=\'t\' AND is_suspicious=\'f\'',
        values: [typeID, regionID]
      }, function(err, result) {

        // Return connection to pool
        done();

        // Handle errors
        if (err) {

          err.module = 'orderProcessor#getStatisticalInformation';

          return callback(err, null);
        } else if (result.rows[0] == undefined) {
          var returnValue = {
            region: regionID,
            type: typeID,
            count: 0,
            stddev: 0.0,
            avg: 0.0
          };
          return callback(null, returnValue);
        } else {
          var returnValue = {
            region: regionID,
            type: typeID,
            count: result.rows[0].count,
            stddev: result.rows[0].stddev,
            avg: result.rows[0].avg
          };
          return callback(null, returnValue);
        }
      });
    }
  });
}


function annotateOrders(resultSet, statisticalProperties) {
  //
  // Add isSuspicious flag to orders
  //

  // Iterate over orders and add isSuspicious flag
  return resultSet.objects.map(processOrder.bind(statisticalProperties));
}

function processOrder(order) {
  //
  // Calculate whether order is suspicious which is an arbitrary definition.
  // Any orders that are outside config.stdDevRejectionMultiplier standard deviations
  // of the mean AND where there are more than 5 orders of like type in the region will be flagged.
  // Flags: True = Yes (suspicious), False = No (not suspicious)
  //

  var delta = 0.0;
  var range = 0.0;

  // First, check if we have more than 5 orders present,
  // then check if price is right or left of the mean value
  if ((this.count > 5) && (order.price > this.avg)) {

    delta = order.price - this.avg;
    range = config.stdDevRejectionMultiplier * this.stddev;

    // If the distance between mean and price is greater than config.stdDevRejectionMultiplier * sigma this must be a suspicious order
    if ((delta > range) && order.bid) {
      order.isSuspicious = true;
    } else {
      order.isSuspicious = false;
    }

  } else if ((this.count > 5) && (order.price < this.avg)) {

    delta = this.avg - order.price;
    range = config.stdDevRejectionMultiplier * this.stddev;

    // If the distance between mean and price is greater than config.stdDevRejectionMultiplier * sigma this must be a suspicious order
    if ((delta > range) && !order.bid) {
      order.isSuspicious = true;
    } else {
      order.isSuspicious = false;
    }

  } else {
    // Not enough datapoints for a reliable guess
    order.isSuspicious = false;
  }

  return order;
}

exports = module.exports = function(resultSet, callback) {
  //
  // Annotate order messages with isSuspicious flag
  //

  if(resultSet.objects.length === 0) {

    // SKIP EMPTY MESSAGES
    return callback(null, resultSet);

  } else {
    getStatisticalInformation(resultSet.regionID, resultSet.typeID, function (err, results) {
      if(err) {
        return callback(err, null);
      } else {
        resultSet.objects = annotateOrders(resultSet, results);
        callback(null, resultSet);
      }
    });
  }
};