UNPKG

algotrader

Version:

Algorithmically trade stocks and options using Robinhood, Yahoo Finance, and more.

331 lines (302 loc) 8.47 kB
const Robinhood = require('./Robinhood'); const LibraryError = require('../../globals/LibraryError'); const request = require('request'); const moment = require('moment'); const async = require('async'); /** * Represents an exchange on which securities are traded. */ class Market extends Robinhood { /** * Creates a new Market object. * @author Torrey Leonard <https://github.com/Ladinn> * @param object - Created using this class' static methods. For example, use 'getByMIC("XNAS")' to create an instance of the Nasdaq. */ constructor(object) { if (!object instanceof Object) throw new Error("Parameter 'object' must be an object."); else { super(); this.parseHours = function(object) { return { isOpen: Boolean(object.is_open), close: object.closes_at === null ? null : new Date(object.closes_at), open: object.opens_at === null ? null : new Date(object.opens_at), extendedOpen: object.extended_opens_at === null ? null : new Date(object.extended_opens_at), extendedClose: object.extended_closes_at === null ? null : new Date(object.extended_closes_at), date: new Date(object.date), nextTradingDay: String(object.next_open_hours), previousTradingDay: String(object.previous_open_hours) } }; this.website = String(object.website); this.city = String(object.city); this.name = String(object.name); this.country = String(object.country); this.acronym = String(object.acronym); this.timezone = String(object.timezone); this.mic = String(object.mic); this.hours = this.parseHours(object.hours); } } /** * Returns a Market object for the given Market Identifier Code (MIC). * See ISO 10383: https://www.iso20022.org/10383/iso-10383-market-identifier-codes * @author Torrey Leonard <https://github.com/Ladinn> * @param {String} code */ static getByMIC(code) { return new Promise((resolve, reject) => { if (!code instanceof String) reject(new Error("Parameter 'code' must be a string.")); request({ uri: "https://api.robinhood.com/markets/" + code + "/" }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, market => { request({ uri: market.todays_hours }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, hours => { market.hours = hours; resolve(new Market(market)); }, reject); }) }, reject); }) }) } /** * Returns a Market object for the given market URL. * @author Torrey Leonard <https://github.com/Ladinn> * @param {String} url */ static getByURL(url) { return new Promise((resolve, reject) => { if (!url instanceof String) reject(new Error("Parameter 'url' must be a string.")); request({ uri: url }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, market => { request({ uri: market.todays_hours }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, hours => { market.hours = hours; resolve(new Market(market)); }, reject); }) }, reject); }) }) } // GET from API /** * Returns an object with hours on the next trading period. * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Promise<Object>} */ getNextTradingHours() { const _this = this; return new Promise((resolve, reject) => { request({ uri: _this.hours.nextTradingDay }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, res => { resolve(_this.parseHours(res)); }, reject); }) }) } /** * Returns an object with hours on the previous trading period. * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Promise<Object>} */ getPreviousTradingHours() { const _this = this; return new Promise((resolve, reject) => { request({ uri: _this.hours.previousTradingDay }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, res => { resolve(_this.parseHours(res)); }, reject); }) }) } /** * Returns an object with hours for the given date. * @author Torrey Leonard <https://github.com/Ladinn> * @param {Date} date * @returns {Promise<Object>} */ getHoursOn(date) { const _this = this; return new Promise((resolve, reject) => { const dateString = moment(date).format("YYYY-MM-DD"); request({ uri: _this.url + "/markets/" + _this.mic + "/hours/" + dateString + "/" }, (error, response, body) => { return Robinhood.handleResponse(error, response, body, null, res => { resolve(_this.parseHours(res)); }, reject); }) }) } /** * Checks whether the market is open on the given date. * @author Torrey Leonard <https://github.com/Ladinn> * @param {Date} date * @returns {Promise.<Boolean>} */ isOpenOn(date) { return this.getHoursOn(date).then(hours => { return Boolean(hours.isOpen); }); }; /** * Returns the next date and time that the market will be open. * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Promise.<Date>} */ getNextOpen() { const _this = this; return new Promise((resolve, reject) => { let next = null; let days = moment().isAfter(_this.getOpen()) ? 1 : 0; async.whilst( () => { return next === null; }, callback => { let newDate = moment().add(days, 'days'); _this.getHoursOn(newDate.toDate()).then(hours => { if (hours.isOpen) next = hours.open; callback(); }).catch(error => reject(new LibraryError(error))); }, () => { resolve(next); }) }) } /** * Returns the next date and time that the market will close. * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Promise<Date>} */ getNextClose() { const _this = this; return new Promise((resolve, reject) => { let next = null; let days = moment().isAfter(_this.getClose()) ? 1 : 0; async.whilst( () => { return next === null; }, callback => { let newDate = moment().add(days, 'days'); _this.getHoursOn(newDate.toDate()).then(hours => { if (hours.isOpen) next = hours.close; callback(); }).catch(error => reject(new LibraryError(error))); }, () => { resolve(next); }) }) } // GET /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {String} */ getWebsite() { return this.website; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {String} */ getCity() { return this.city; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {String} */ getName() { return this.name; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {String} */ getCountry() { return this.country; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {String} */ getCode() { return this.mic; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {String} */ getAcronym() { return this.acronym; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {{isOpen: boolean, close: Date, open: Date, extendedOpen: Date, extendedClose: Date, date: Date}} */ getHours() { return this.hours; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Date} */ getClose() { return this.hours.close; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Date} */ getOpen() { return this.hours.open; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Date} */ getExtendedClose() { return this.hours.extendedClose; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Date} */ getExtendedOpen() { return this.hours.extendedOpen; } // BOOLEAN /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Boolean} */ isOpenToday() { return this.hours.isOpen; } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Boolean} */ isOpenNow() { return moment().isAfter(this.getOpen()) && moment().isBefore(this.getClose()); } /** * @author Torrey Leonard <https://github.com/Ladinn> * @returns {Boolean} */ isExtendedOpenNow() { return moment().isAfter(this.getExtendedOpen()) && moment().isBefore(this.getExtendedClose()) } } module.exports = Market;