1 | const LibraryError = require('../globals/LibraryError');
|
2 | const News = require('./News');
|
3 | const Quote = require('../globals/Quote');
|
4 | const EventEmitter = require('events');
|
5 | const request = require('request');
|
6 | const socket = require('socket.io-client');
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | class Stream extends EventEmitter {
|
18 |
|
19 | |
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | constructor(symbols, options) {
|
32 | super();
|
33 | this.symbols = symbols;
|
34 | if (options) {
|
35 | this.iex = Boolean(options.iex) || false;
|
36 | this.iexType = String(options.iexType) || "tops";
|
37 | this.news = Boolean(options.news) || false;
|
38 | this.allHeadlines = Boolean(options.allHeadlines) || false;
|
39 | this.newsApiKey = String(options.newsApiKey) || false;
|
40 | this.newsArray = [];
|
41 | }
|
42 | }
|
43 |
|
44 | |
45 |
|
46 |
|
47 |
|
48 | start() {
|
49 | const _this = this;
|
50 | this.request = request.get({
|
51 | uri: "https://streamerapi.finance.yahoo.com/streamer/1.0",
|
52 | qs: {
|
53 | s: _this.symbols.join(','),
|
54 | k: _this._getYahooKeys().join(','),
|
55 | callback: 'parent.yfs_u1f',
|
56 | mktmcb: 'parent.yfs_mktmcb',
|
57 | gencallback: 'parent.yfs_gencb',
|
58 | mu: '1',
|
59 | lang: 'en-US',
|
60 | region: 'US',
|
61 | localize: '0'
|
62 | },
|
63 | qsStringifyOptions: {
|
64 | encode: false
|
65 | },
|
66 | forever: true
|
67 | }).on('error', error => {
|
68 | _this.emit('error', error)
|
69 | }).on('response', response => {
|
70 | if (response.statusCode === 404) _this.emit('error', new LibraryError("Yahoo Finance streamer responded with status code: 404.\n\tThis is typically a result of an invalid equity symbol in the query."));
|
71 | else if (response.statusCode !== 200) _this.emit('error', new LibraryError("Yahoo Finance streamer responded with status code: " + response.statusCode));
|
72 | else _this.emit('response', response);
|
73 | }).on('data', data => {
|
74 | try {
|
75 | let input = data.toString().split('yfs_u1f(');
|
76 | input.shift();
|
77 | input.forEach(i => {
|
78 | const quote = _this._createQuote(i);
|
79 | _this.emit('quote', quote);
|
80 | if (_this.news) {
|
81 | let options = {
|
82 | country: "us",
|
83 | pageSize: 100,
|
84 | };
|
85 | if (!_this.allHeadlines) options.q = quote.getSymbol();
|
86 | else options.category = "business";
|
87 | News.getHeadlines(_this.newsApiKey, options).then(array => {
|
88 | array.forEach(news => {
|
89 | if (_this.newsArray.indexOf(news) === -1) {
|
90 | _this.emit('news', news);
|
91 | _this.newsArray.push(news);
|
92 | }
|
93 | })
|
94 | }).catch(error => _this.emit('error', new LibraryError(error)));
|
95 | }
|
96 | });
|
97 | } catch (error) {
|
98 | _this.emit('error', error);
|
99 | }
|
100 | });
|
101 | if (_this.iex) {
|
102 | _this.socket = socket("https://ws-api.iextrading.com/1.0/" + _this.iexType);
|
103 | _this.socket.on('connect', () => {
|
104 | _this.socket.emit('subscribe', _this.symbols.join(','));
|
105 | });
|
106 | _this.socket.on('message', data => {
|
107 | _this.emit('iex', data);
|
108 | });
|
109 | }
|
110 | }
|
111 |
|
112 | |
113 |
|
114 |
|
115 |
|
116 |
|
117 | stop() {
|
118 | this.request.abort();
|
119 | if(this.iex){
|
120 | this.socket.disconnect();
|
121 | }
|
122 | }
|
123 |
|
124 | |
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 | _createQuote(input) {
|
131 | const yahooKeys = {
|
132 | a00: 'ask',
|
133 | a50: 'askSize',
|
134 | b00: 'bid',
|
135 | b60: 'bidSize',
|
136 | c10: 'change',
|
137 | c63: 'changeRealTime',
|
138 | c64: 'disputedChangeRealTimeAfterHours',
|
139 | c85: 'changeRealTimeAfterHours',
|
140 | c86: 'percentChangeRealTimeAfterHours',
|
141 | g53: 'dayLow',
|
142 | h53: 'dayHigh',
|
143 | j10: 'marketCap',
|
144 | l84: 'priceRealTime',
|
145 | l86: 'priceRealTimeAfterHours',
|
146 | p20: 'percentChange',
|
147 | p43: 'percentChangeRealTime',
|
148 | p44: 'percentChangeRealTimeAfterHours',
|
149 | t53: 'disputedTimestampForCommodities',
|
150 | t54: 'disputedTimestampForStocks',
|
151 | v53: 'volume',
|
152 | v00: 'volume2',
|
153 | l10: 'lastSalePrice',
|
154 | t10: 'lastSaleTime',
|
155 | l90: 'ecnQuoteLastValue'
|
156 | };
|
157 | for (const key in yahooKeys) {
|
158 | input = input.replace(key, '"' + yahooKeys[key] + '"');
|
159 | }
|
160 | const json = JSON.parse(input.split(');')[0]);
|
161 |
|
162 | const symbol = Object.keys(json)[0];
|
163 | const object = json[symbol];
|
164 |
|
165 | let quote = {
|
166 | symbol: symbol,
|
167 | date: new Date(),
|
168 | source: "Yahoo Finance",
|
169 | price: {
|
170 | last: Number(object.priceRealTimeAfterHours) || Number(object.priceRealTime) || Number(object.lastSalePrice),
|
171 | high: Number(object.dayHigh),
|
172 | low: Number(object.dayLow)
|
173 | },
|
174 | dom: {
|
175 | bid: {
|
176 | price: Number(object.bid),
|
177 | size: Number(object.bidSize)
|
178 | },
|
179 | ask: {
|
180 | price: Number(object.ask),
|
181 | size: Number(object.askSize)
|
182 | }
|
183 | },
|
184 | meta: {
|
185 | change: object.change || object.changeRealTime,
|
186 | percentChange: object.percentChange || object.percentChangeRealTime,
|
187 | marketCap: object.marketCap
|
188 | },
|
189 | original: json
|
190 | };
|
191 |
|
192 | if (object.volume) quote.price.volume = Number(object.volume.replace(/,/g, ''));
|
193 | if (object.volume2) quote.price.volume = Number(object.volume2.replace(/,/g, ''));
|
194 |
|
195 | return new Quote(quote);
|
196 |
|
197 | }
|
198 |
|
199 | |
200 |
|
201 |
|
202 |
|
203 |
|
204 | _getYahooKeys() {
|
205 | const keys = {
|
206 | Ask: 'a00',
|
207 | AskSize: 'a50',
|
208 | Bid: 'b00',
|
209 | BidSize: 'b60',
|
210 | Change: 'c10',
|
211 | ChangeRealTime: 'c63',
|
212 | DisputedChangeRealTimeAfterHours: 'c64',
|
213 | ChangeRealTimeAfterHours: 'c85',
|
214 | PercentChangeRealTimeAfterHours: 'p44',
|
215 | DayLow: 'g53',
|
216 | DayHigh: 'h53',
|
217 | MarketCap: 'j10',
|
218 | PriceRealTime: 'l84',
|
219 | PriceRealTimeAfterHours: 'l86',
|
220 | PercentChange: 'p20',
|
221 | PercentChangeRealTime: 'p43',
|
222 | DisputedTimestampForCommodities: 't53',
|
223 | DisputedTimestampForStocks: 't54',
|
224 | Volume: 'v53',
|
225 | Volume2: 'v00',
|
226 | LastSalePrice: 'l10',
|
227 | LastSaleTime: 't10',
|
228 | EcnQuoteLastValue: 'l90',
|
229 | };
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 | return [
|
247 | 'a00', 'a50', 'b00', 'b60', 'c10', 'c63', 'c64', 'c85', 'p44', 'g53', 'h53', 'j10', 'l84', 'l86', 'p20', 'p43', 't53', 't54', 'v53', 'v00', 'l10', 't10', 'l90'
|
248 | ]
|
249 | }
|
250 |
|
251 | }
|
252 |
|
253 | module.exports = Stream; |
\ | No newline at end of file |