UNPKG

bot18

Version:

A high-frequency cryptocurrency trading bot by Zenbot creator @carlos8f

228 lines (194 loc) 5.72 kB
/* * Copyright (c) 2012 Mathieu Turcotte * Licensed under the MIT license. */ var events = require('events'); var util = require('util'); var Backoff = require('./backoff'); var FibonacciBackoffStrategy = require('./strategy/fibonacci'); /** * Returns true if the specified value is a function * @param val Variable to test. * @return Whether variable is a function. */ function isFunction(val) { return typeof val == 'function'; } /** * Manages the calling of a function in a backoff loop. * @param fn Function to wrap in a backoff handler. * @param args Array of function's arguments. * @param callback Function's callback. * @constructor */ function FunctionCall(fn, args, callback) { events.EventEmitter.call(this); if (!isFunction(fn)) { throw new Error('fn should be a function.' + 'Actual: ' + typeof fn); } if (!isFunction(callback)) { throw new Error('callback should be a function.' + 'Actual: ' + typeof fn); } this.function_ = fn; this.arguments_ = args; this.callback_ = callback; this.results_ = []; this.backoff_ = null; this.strategy_ = null; this.failAfter_ = -1; this.state_ = FunctionCall.State_.PENDING; } util.inherits(FunctionCall, events.EventEmitter); /** * Enum of states in which the FunctionCall can be. * @private */ FunctionCall.State_ = { PENDING: 0, RUNNING: 1, COMPLETED: 2, ABORTED: 3 }; /** * @return Whether the call is pending. */ FunctionCall.prototype.isPending = function() { return this.state_ == FunctionCall.State_.PENDING; }; /** * @return Whether the call is in progress. */ FunctionCall.prototype.isRunning = function() { return this.state_ == FunctionCall.State_.RUNNING; }; /** * @return Whether the call is completed. */ FunctionCall.prototype.isCompleted = function() { return this.state_ == FunctionCall.State_.COMPLETED; }; /** * @return Whether the call is aborted. */ FunctionCall.prototype.isAborted = function() { return this.state_ == FunctionCall.State_.ABORTED; }; /** * Sets the backoff strategy. * @param strategy The backoff strategy to use. * @return Itself for chaining. */ FunctionCall.prototype.setStrategy = function(strategy) { if (!this.isPending()) { throw new Error('FunctionCall in progress.'); } this.strategy_ = strategy; return this; }; /** * Returns all intermediary results returned by the wrapped function since * the initial call. * @return An array of intermediary results. */ FunctionCall.prototype.getResults = function() { return this.results_.concat(); }; /** * Sets the backoff limit. * @param maxNumberOfRetry The maximum number of backoffs. * @return Itself for chaining. */ FunctionCall.prototype.failAfter = function(maxNumberOfRetry) { if (!this.isPending()) { throw new Error('FunctionCall in progress.'); } this.failAfter_ = maxNumberOfRetry; return this; }; /** * Aborts the call. */ FunctionCall.prototype.abort = function() { if (this.isCompleted()) { throw new Error('FunctionCall already completed.'); } if (this.isRunning()) { this.backoff_.reset(); } this.state_ = FunctionCall.State_.ABORTED; }; /** * Initiates the call to the wrapped function. * @param backoffFactory Optional factory function used to create the backoff * instance. */ FunctionCall.prototype.start = function(backoffFactory) { if (this.isAborted()) { throw new Error('FunctionCall aborted.'); } else if (!this.isPending()) { throw new Error('FunctionCall already started.'); } var strategy = this.strategy_ || new FibonacciBackoffStrategy(); this.backoff_ = backoffFactory ? backoffFactory(strategy) : new Backoff(strategy); this.backoff_.on('ready', this.doCall_.bind(this)); this.backoff_.on('fail', this.doCallback_.bind(this)); this.backoff_.on('backoff', this.handleBackoff_.bind(this)); if (this.failAfter_ > 0) { this.backoff_.failAfter(this.failAfter_); } this.state_ = FunctionCall.State_.RUNNING; this.doCall_(); }; /** * Calls the wrapped function. * @private */ FunctionCall.prototype.doCall_ = function() { var eventArgs = ['call'].concat(this.arguments_); events.EventEmitter.prototype.emit.apply(this, eventArgs); var callback = this.handleFunctionCallback_.bind(this); this.function_.apply(null, this.arguments_.concat(callback)); }; /** * Calls the wrapped function's callback with the last result returned by the * wrapped function. * @private */ FunctionCall.prototype.doCallback_ = function() { var args = this.results_[this.results_.length - 1]; this.callback_.apply(null, args); }; /** * Handles wrapped function's completion. This method acts as a replacement * for the original callback function. * @private */ FunctionCall.prototype.handleFunctionCallback_ = function() { if (this.isAborted()) { return; } var args = Array.prototype.slice.call(arguments); this.results_.push(args); // Save callback arguments. events.EventEmitter.prototype.emit.apply(this, ['callback'].concat(args)); if (args[0]) { this.backoff_.backoff(args[0]); } else { this.state_ = FunctionCall.State_.COMPLETED; this.doCallback_(); } }; /** * Handles backoff event. * @param number Backoff number. * @param delay Backoff delay. * @param err The error that caused the backoff. * @private */ FunctionCall.prototype.handleBackoff_ = function(number, delay, err) { this.emit('backoff', number, delay, err); }; module.exports = FunctionCall;