From 17c2b0d589a55fdca14eaf6bc1eb1c8169075347 Mon Sep 17 00:00:00 2001 From: Bogdan Guban Date: Tue, 21 Sep 2021 23:45:38 +0300 Subject: [PATCH 1/3] Added base functionality --- lib/iex/api/config/client.rb | 2 + lib/iex/cloud/request.rb | 19 ++++++- lib/iex/endpoints/quote.rb | 13 +++++ spec/fixtures/iex/stream_quote/spy.yml | 61 +++++++++++++++++++++ spec/iex/endpoints/quote_spec.rb | 73 ++++++++++++++++---------- 5 files changed, 139 insertions(+), 29 deletions(-) create mode 100644 spec/fixtures/iex/stream_quote/spy.yml diff --git a/lib/iex/api/config/client.rb b/lib/iex/api/config/client.rb index 9c520a4..41bf614 100644 --- a/lib/iex/api/config/client.rb +++ b/lib/iex/api/config/client.rb @@ -6,6 +6,7 @@ module Client ca_file ca_path endpoint + sse_endpoint open_timeout proxy publishable_token @@ -24,6 +25,7 @@ def reset! self.ca_file = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_FILE : nil self.ca_path = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_DIR : nil self.endpoint = 'https://cloud.iexapis.com/v1' + self.sse_endpoint = 'https://cloud-sse.iexapis.com/v1' self.publishable_token = ENV['IEX_API_PUBLISHABLE_TOKEN'] self.secret_token = ENV['IEX_API_SECRET_TOKEN'] self.user_agent = "IEX Ruby Client/#{IEX::VERSION}" diff --git a/lib/iex/cloud/request.rb b/lib/iex/cloud/request.rb index 4136f00..6c2fe55 100644 --- a/lib/iex/cloud/request.rb +++ b/lib/iex/cloud/request.rb @@ -1,10 +1,25 @@ module IEX module Cloud module Request + STREAM_EVENT_DELIMITER = "\r\n\r\n".freeze + def get(path, options = {}) request(:get, path, options) end + def get_stream(path, options = {}) + buffer = "" + event_parser = Proc.new do |chunk| + events = (buffer + chunk).lines(STREAM_EVENT_DELIMITER) + buffer = events.last.end_with?(STREAM_EVENT_DELIMITER) ? '' : events.delete_at(-1) + events.each do |event| + yield JSON.parse(event.gsub(/\Adata: /, '')) + end + end + + request(:get, path, { endpoint: sse_endpoint, request: { on_data: event_parser } }.merge(options)) + end + def post(path, options = {}) request(:post, path, options) end @@ -20,8 +35,9 @@ def delete(path, options = {}) private def request(method, path, options) - path = [endpoint, path].join('/') + path = [options.delete(:endpoint) || endpoint, path].join('/') response = connection.send(method) do |request| + request.options.merge!(options.delete(:request)) if options.key?(:request) case method when :get, :delete request.url(path, options) @@ -29,7 +45,6 @@ def request(method, path, options) request.path = path request.body = options.to_json unless options.empty? end - request.options.merge!(options.delete(:request)) if options.key?(:request) end response.body end diff --git a/lib/iex/endpoints/quote.rb b/lib/iex/endpoints/quote.rb index 664f3b9..e711b98 100644 --- a/lib/iex/endpoints/quote.rb +++ b/lib/iex/endpoints/quote.rb @@ -6,6 +6,19 @@ def quote(symbol, options = {}) rescue Faraday::ResourceNotFound => e raise IEX::Errors::SymbolNotFoundError.new(symbol, e.response[:body]) end + + # @param symbols - a list of symbols + # @param options[:interval] sets intervals such as 1Second, 5Second, or 1Minute + def stream_quote(symbols, options = {}) + options[:symbols] = Array(symbols).join(',') + interval = options.delete(:interval) + + get_stream("stocksUS#{interval}", { token: secret_token }.merge(options)) do |payload| + payload.each do |quote| + yield IEX::Resources::Quote.new(quote) + end + end + end end end end diff --git a/spec/fixtures/iex/stream_quote/spy.yml b/spec/fixtures/iex/stream_quote/spy.yml new file mode 100644 index 0000000..367fae1 --- /dev/null +++ b/spec/fixtures/iex/stream_quote/spy.yml @@ -0,0 +1,61 @@ +--- +http_interactions: +- request: + method: get + uri: https://cloud-sse.iexapis.com/v1/stocksUS5Second?symbols=SPY&token=test-iex-api-secret-token + body: + encoding: US-ASCII + string: '' + headers: + Accept: + - application/json; charset=utf-8 + Content-Type: + - application/json; charset=utf-8 + User-Agent: + - IEX Ruby Client/1.5.1 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + response: + status: + code: 200 + message: OK + headers: + Server: + - nginx + Date: + - Tue, 21 Sep 2021 20:29:16 GMT + Content-Type: + - application/json; charset=utf-8 + Transfer-Encoding: + - chunked + Connection: + - keep-alive + Set-Cookie: + - ctoken=609af7a37a89493e802c2218673c7783; Max-Age=43200; Path=/; Expires=Wed, + 22 Sep 2021 08:29:16 GMT + Iexcloud-Messages-Used: + - '2' + Iexcloud-Credits-Used: + - '2' + Iexcloud-Premium-Messages-Used: + - '0' + Iexcloud-Premium-Credits-Used: + - '0' + X-Content-Type-Options: + - nosniff + Strict-Transport-Security: + - max-age=15768000 + Access-Control-Allow-Origin: + - "*" + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Methods: + - GET, OPTIONS + Access-Control-Allow-Headers: + - Origin, X-Requested-With, Content-Type, Accept, Request-Source + body: + encoding: ASCII-8BIT + string: "data: [{\"avgTotalVolume\":68745683,\"calculationPrice\":\"close\",\"change\":-0.41,\"changePercent\":-0.00094,\"close\":433.63,\"closeSource\":\"official\",\"closeTime\":1632254400206,\"companyName\":\"SSgA Active Trust - S&P 500 ETF TRUST ETF\",\"currency\":\"USD\",\"delayedPrice\":433.72,\"delayedPriceTime\":1632254399904,\"extendedChange\":-0.9,\"extendedChangePercent\":-0.00208,\"extendedPrice\":432.73,\"extendedPriceTime\":1632256104619,\"high\":437.91,\"highSource\":\"15 minute delayed price\",\"highTime\":1632254404318,\"iexAskPrice\":432.81,\"iexAskSize\":500,\"iexBidPrice\":432.76,\"iexBidSize\":500,\"iexClose\":433.63,\"iexCloseTime\":1632254398127,\"iexLastUpdated\":1632256104619,\"iexMarketPercent\":0.013629911270082975,\"iexOpen\":432.73,\"iexOpenTime\":1632256104619,\"iexRealtimePrice\":432.73,\"iexRealtimeSize\":1,\"iexVolume\":1244956,\"lastTradeTime\":1632254418262,\"latestPrice\":433.63,\"latestSource\":\"Close\",\"latestTime\":\"September 21, 2021\",\"latestUpdate\":1632254400206,\"latestVolume\":91339993,\"low\":433.07,\"lowSource\":\"15 minute delayed price\",\"lowTime\":1632236056745,\"marketCap\":397369859400,\"oddLotDelayedPrice\":433.67,\"oddLotDelayedPriceTime\":1632254396989,\"open\":436.63,\"openTime\":1632231000366,\"openSource\":\"official\",\"peRatio\":null,\"previousClose\":434.04,\"previousVolume\":166445534,\"primaryExchange\":\"NYSE ARCA\",\"symbol\":\"SPY\",\"volume\":91339993,\"week52High\":452.6,\"week52Low\":315.36,\"ytdChange\":0.1713227149749243,\"isUSMarketOpen\":false}]\r\n\r\n" + http_version: + recorded_at: Tue, 21 Sep 2021 20:29:16 GMT +recorded_with: VCR 5.1.0 diff --git a/spec/iex/endpoints/quote_spec.rb b/spec/iex/endpoints/quote_spec.rb index 6b6ca4d..8490d64 100644 --- a/spec/iex/endpoints/quote_spec.rb +++ b/spec/iex/endpoints/quote_spec.rb @@ -2,39 +2,58 @@ describe IEX::Resources::Quote do include_context 'client' - context 'known symbol', vcr: { cassette_name: 'quote/msft' } do - subject do - client.quote('MSFT') - end - it 'retrieves a quote' do - expect(subject.symbol).to eq 'MSFT' - expect(subject.company_name).to eq 'Microsoft Corp.' - expect(subject.market_cap).to eq 915_754_985_600 - end - it 'coerces numbers' do - expect(subject.latest_price).to eq 119.36 - expect(subject.change).to eq(-0.61) - expect(subject.week_52_high).to eq 120.82 - expect(subject.week_52_low).to eq 87.73 - expect(subject.change_percent).to eq(-0.00508) - expect(subject.change_percent_s).to eq '-0.51%' - expect(subject.extended_change_percent).to eq(-0.00008) - expect(subject.extended_change_percent_s).to eq '-0.01%' + + describe '#quote' do + context 'known symbol', vcr: { cassette_name: 'quote/msft' } do + subject do + client.quote('MSFT') + end + it 'retrieves a quote' do + expect(subject.symbol).to eq 'MSFT' + expect(subject.company_name).to eq 'Microsoft Corp.' + expect(subject.market_cap).to eq 915_754_985_600 + end + it 'coerces numbers' do + expect(subject.latest_price).to eq 119.36 + expect(subject.change).to eq(-0.61) + expect(subject.week_52_high).to eq 120.82 + expect(subject.week_52_low).to eq 87.73 + expect(subject.change_percent).to eq(-0.00508) + expect(subject.change_percent_s).to eq '-0.51%' + expect(subject.extended_change_percent).to eq(-0.00008) + expect(subject.extended_change_percent_s).to eq '-0.01%' + end + it 'coerces times' do + expect(subject.latest_update).to eq 1_554_408_000_193 + expect(subject.latest_update_t).to eq Time.at(1_554_408_000) + expect(subject.iex_last_updated).to eq 1_554_407_999_529 + expect(subject.iex_last_updated_t).to eq Time.at(1_554_407_999) + end end - it 'coerces times' do - expect(subject.latest_update).to eq 1_554_408_000_193 - expect(subject.latest_update_t).to eq Time.at(1_554_408_000) - expect(subject.iex_last_updated).to eq 1_554_407_999_529 - expect(subject.iex_last_updated_t).to eq Time.at(1_554_407_999) + + context 'invalid symbol', vcr: { cassette_name: 'quote/invalid' } do + subject do + client.quote('INVALID') + end + it 'fails with SymbolNotFoundError' do + expect { subject }.to raise_error IEX::Errors::SymbolNotFoundError, 'Symbol INVALID Not Found' + end end end - context 'invalid symbol', vcr: { cassette_name: 'quote/invalid' } do + describe '#stream_quote' do subject do - client.quote('INVALID') + quotes = [] + client.stream_quote('SPY', interval: '5Second') do |quote| + quotes << quote + end + + quotes.first end - it 'fails with SymbolNotFoundError' do - expect { subject }.to raise_error IEX::Errors::SymbolNotFoundError, 'Symbol INVALID Not Found' + + it 'retrieves a quote', vcr: { cassette_name: 'stream_quote/spy' } do + expect(subject.symbol).to eq('SPY') + expect(subject.close).to eq(433.63) end end end From 8776ee5dc7cc4e74bc75d1f5ae727c7a660d5ba4 Mon Sep 17 00:00:00 2001 From: Bogdan Guban Date: Wed, 22 Sep 2021 13:34:35 +0300 Subject: [PATCH 2/3] fixes --- CHANGELOG.md | 1 + README.md | 15 +++++++++++++++ iex-ruby-client.gemspec | 2 +- lib/iex/api/config/client.rb | 2 -- lib/iex/cloud/request.rb | 11 ++++++++--- spec/fixtures/iex/stream_quote/spy.yml | 2 +- spec/iex/endpoints/quote_spec.rb | 9 ++++++--- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 563b372..0ca4500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 1.5.1 (Next) * Your contribution here. +* [#108](https://github.com/dblock/iex-ruby-client/pull/108): Added support for quote streaming endpoint - [@bguban](https://github.com/bguban). ### 1.5.0 (2021/08/15) * [#105](https://github.com/dblock/iex-ruby-client/pull/105): Added support for fetching latest foreign exchange rates - [@mathu97](https://github.com/mathu97). diff --git a/README.md b/README.md index cec7520..adece4f 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,21 @@ quote.change_percent_s # '+0.42%' See [#quote](https://iexcloud.io/docs/api/#quote) for detailed documentation or [quote.rb](lib/iex/resources/quote.rb) for returned fields. +### Stream quotes + +Streams quotes in real-time. + +`interval` option lets you limit amount of updates. Possible values: `1Second 5Second 1Minute` + +```ruby +client.stream_quote(['SPY', 'MSFT'], interval: '5Second') do |quote| + quote.latest_price # 90.165 + quote.symbol # 'MSFT' +end +``` + +See [#streaming-data](https://iexcloud.io/docs/api/#streaming-data) for detailed documentation or [quote.rb](lib/iex/resources/quote.rb) for returned fields. + ### Get a OHLC (Open, High, Low, Close) price Fetches a single stock OHLC price. Open and Close prices contain timestamp. diff --git a/iex-ruby-client.gemspec b/iex-ruby-client.gemspec index 9814760..1289cf6 100644 --- a/iex-ruby-client.gemspec +++ b/iex-ruby-client.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.add_dependency 'faraday', '>= 0.17' s.add_dependency 'faraday_middleware' s.add_dependency 'hashie' - s.add_dependency 'money_helper' + s.add_dependency 'money_helper', '~>1' s.add_development_dependency 'rake', '~> 10' s.add_development_dependency 'rspec' s.add_development_dependency 'rubocop', '0.72.0' diff --git a/lib/iex/api/config/client.rb b/lib/iex/api/config/client.rb index 41bf614..9c520a4 100644 --- a/lib/iex/api/config/client.rb +++ b/lib/iex/api/config/client.rb @@ -6,7 +6,6 @@ module Client ca_file ca_path endpoint - sse_endpoint open_timeout proxy publishable_token @@ -25,7 +24,6 @@ def reset! self.ca_file = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_FILE : nil self.ca_path = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_DIR : nil self.endpoint = 'https://cloud.iexapis.com/v1' - self.sse_endpoint = 'https://cloud-sse.iexapis.com/v1' self.publishable_token = ENV['IEX_API_PUBLISHABLE_TOKEN'] self.secret_token = ENV['IEX_API_SECRET_TOKEN'] self.user_agent = "IEX Ruby Client/#{IEX::VERSION}" diff --git a/lib/iex/cloud/request.rb b/lib/iex/cloud/request.rb index 6c2fe55..a000b52 100644 --- a/lib/iex/cloud/request.rb +++ b/lib/iex/cloud/request.rb @@ -8,8 +8,8 @@ def get(path, options = {}) end def get_stream(path, options = {}) - buffer = "" - event_parser = Proc.new do |chunk| + buffer = '' + event_parser = proc do |chunk| events = (buffer + chunk).lines(STREAM_EVENT_DELIMITER) buffer = events.last.end_with?(STREAM_EVENT_DELIMITER) ? '' : events.delete_at(-1) events.each do |event| @@ -17,7 +17,11 @@ def get_stream(path, options = {}) end end - request(:get, path, { endpoint: sse_endpoint, request: { on_data: event_parser } }.merge(options)) + options = { + endpoint: endpoint.gsub('://cloud.', '://cloud-sse.'), + request: { on_data: event_parser } + }.merge(options) + request(:get, path, options) end def post(path, options = {}) @@ -35,6 +39,7 @@ def delete(path, options = {}) private def request(method, path, options) + options = options.dup path = [options.delete(:endpoint) || endpoint, path].join('/') response = connection.send(method) do |request| request.options.merge!(options.delete(:request)) if options.key?(:request) diff --git a/spec/fixtures/iex/stream_quote/spy.yml b/spec/fixtures/iex/stream_quote/spy.yml index 367fae1..3ceea19 100644 --- a/spec/fixtures/iex/stream_quote/spy.yml +++ b/spec/fixtures/iex/stream_quote/spy.yml @@ -55,7 +55,7 @@ http_interactions: - Origin, X-Requested-With, Content-Type, Accept, Request-Source body: encoding: ASCII-8BIT - string: "data: [{\"avgTotalVolume\":68745683,\"calculationPrice\":\"close\",\"change\":-0.41,\"changePercent\":-0.00094,\"close\":433.63,\"closeSource\":\"official\",\"closeTime\":1632254400206,\"companyName\":\"SSgA Active Trust - S&P 500 ETF TRUST ETF\",\"currency\":\"USD\",\"delayedPrice\":433.72,\"delayedPriceTime\":1632254399904,\"extendedChange\":-0.9,\"extendedChangePercent\":-0.00208,\"extendedPrice\":432.73,\"extendedPriceTime\":1632256104619,\"high\":437.91,\"highSource\":\"15 minute delayed price\",\"highTime\":1632254404318,\"iexAskPrice\":432.81,\"iexAskSize\":500,\"iexBidPrice\":432.76,\"iexBidSize\":500,\"iexClose\":433.63,\"iexCloseTime\":1632254398127,\"iexLastUpdated\":1632256104619,\"iexMarketPercent\":0.013629911270082975,\"iexOpen\":432.73,\"iexOpenTime\":1632256104619,\"iexRealtimePrice\":432.73,\"iexRealtimeSize\":1,\"iexVolume\":1244956,\"lastTradeTime\":1632254418262,\"latestPrice\":433.63,\"latestSource\":\"Close\",\"latestTime\":\"September 21, 2021\",\"latestUpdate\":1632254400206,\"latestVolume\":91339993,\"low\":433.07,\"lowSource\":\"15 minute delayed price\",\"lowTime\":1632236056745,\"marketCap\":397369859400,\"oddLotDelayedPrice\":433.67,\"oddLotDelayedPriceTime\":1632254396989,\"open\":436.63,\"openTime\":1632231000366,\"openSource\":\"official\",\"peRatio\":null,\"previousClose\":434.04,\"previousVolume\":166445534,\"primaryExchange\":\"NYSE ARCA\",\"symbol\":\"SPY\",\"volume\":91339993,\"week52High\":452.6,\"week52Low\":315.36,\"ytdChange\":0.1713227149749243,\"isUSMarketOpen\":false}]\r\n\r\n" + string: "data: [{\"avgTotalVolume\":68745683,\"calculationPrice\":\"close\",\"change\":-0.41,\"changePercent\":-0.00094,\"close\":433.63,\"closeSource\":\"official\",\"closeTime\":1632254400206,\"companyName\":\"SSgA Active Trust - S&P 500 ETF TRUST ETF\",\"currency\":\"USD\",\"delayedPrice\":433.72,\"delayedPriceTime\":1632254399904,\"extendedChange\":-0.9,\"extendedChangePercent\":-0.00208,\"extendedPrice\":432.73,\"extendedPriceTime\":1632256104619,\"high\":437.91,\"highSource\":\"15 minute delayed price\",\"highTime\":1632254404318,\"iexAskPrice\":432.81,\"iexAskSize\":500,\"iexBidPrice\":432.76,\"iexBidSize\":500,\"iexClose\":433.63,\"iexCloseTime\":1632254398127,\"iexLastUpdated\":1632256104619,\"iexMarketPercent\":0.013629911270082975,\"iexOpen\":432.73,\"iexOpenTime\":1632256104619,\"iexRealtimePrice\":432.73,\"iexRealtimeSize\":1,\"iexVolume\":1244956,\"lastTradeTime\":1632254418262,\"latestPrice\":433.63,\"latestSource\":\"Close\",\"latestTime\":\"September 21, 2021\",\"latestUpdate\":1632254400206,\"latestVolume\":91339993,\"low\":433.07,\"lowSource\":\"15 minute delayed price\",\"lowTime\":1632236056745,\"marketCap\":397369859400,\"oddLotDelayedPrice\":433.67,\"oddLotDelayedPriceTime\":1632254396989,\"open\":436.63,\"openTime\":1632231000366,\"openSource\":\"official\",\"peRatio\":null,\"previousClose\":434.04,\"previousVolume\":166445534,\"primaryExchange\":\"NYSE ARCA\",\"symbol\":\"SPY\",\"volume\":91339993,\"week52High\":452.6,\"week52Low\":315.36,\"ytdChange\":0.1713227149749243,\"isUSMarketOpen\":false}]\r\n\r\ndata: [{\"avgTotalVolume\":68745683,\"calculationPrice\":\"close\",\"change\":-0.41,\"changePercent\":-0.00094,\"close\":433.63,\"closeSource\":\"official\",\"closeTime\":1632254400206,\"companyName\":\"SSgA Active Trust - S&P 500 ETF TRUST ETF\",\"currency\":\"USD\",\"delayedPrice\":433.72,\"delayedPriceTime\":1632254399904,\"extendedChange\":-0.9,\"extendedChangePercent\":-0.00208,\"extendedPrice\":432.73,\"extendedPriceTime\":1632256104619,\"high\":437.91,\"highSource\":\"15 minute delayed price\",\"highTime\":1632254404318,\"iexAskPrice\":432.81,\"iexAskSize\":500,\"iexBidPrice\":432.76,\"iexBidSize\":500,\"iexClose\":433.63,\"iexCloseTime\":1632254398127,\"iexLastUpdated\":1632256104619,\"iexMarketPercent\":0.013629911270082975,\"iexOpen\":432.73,\"iexOpenTime\":1632256104619,\"iexRealtimePrice\":432.73,\"iexRealtimeSize\":1,\"iexVolume\":1244956,\"lastTradeTime\":1632254418262,\"latestPrice\":433.63,\"latestSource\":\"Close\",\"latestTime\":\"September 21, 2021\",\"latestUpdate\":1632254400206,\"latestVolume\":91339993,\"low\":433.07,\"lowSource\":\"15 minute delayed price\",\"lowTime\":1632236056745,\"marketCap\":397369859400,\"oddLotDelayedPrice\":433.67,\"oddLotDelayedPriceTime\":1632254396989,\"open\":436.63,\"openTime\":1632231000366,\"openSource\":\"official\",\"peRatio\":null,\"previousClose\":434.04,\"previousVolume\":166445534,\"primaryExchange\":\"NYSE ARCA\",\"symbol\":\"SPY\",\"volume\":91339993,\"week52High\":452.6,\"week52Low\":315.36,\"ytdChange\":0.1713227149749243,\"isUSMarketOpen\":false}]\r\n\r\ndata: [{\"avgTotalVolume\":68745683,\"calculationPrice\":\"close\",\"change\":-0.41,\"changePercent\":-0.00094,\"close\":433.63,\"closeSource\":\"official\",\"closeTime\":1632254400206,\"companyName\":\"SSgA Active Trust - S&P 500 ETF TRUST ETF\",\"currency\":\"USD\",\"delayedPrice\":433.72,\"delayedPriceTime\":1632254399904,\"extendedChange\":-0.9,\"extendedChangePercent\":-0.00208,\"extendedPrice\":432.73,\"extendedPriceTime\":1632256104619,\"high\":437.91,\"highSource\":\"15 minute delayed price\",\"highTime\":1632254404318,\"iexAskPrice\":432.81,\"iexAskSize\":500,\"iexBidPrice\":432.76,\"iexBidSize\":500,\"iexClose\":433.63,\"iexCloseTime\":1632254398127,\"iexLastUpdated\":1632256104619,\"iexMarketPercent\":0.013629911270082975,\"iexOpen\":432.73,\"iexOpenTime\":1632256104619,\"iexRealtimePrice\":432.73,\"iexRealtimeSize\":1,\"iexVolume\":1244956,\"lastTradeTime\":1632254418262,\"latestPrice\":433.63,\"latestSource\":\"Close\",\"latestTime\":\"September 21, 2021\",\"latestUpdate\":1632254400206,\"latestVolume\":91339993,\"low\":433.07,\"lowSource\":\"15 minute delayed price\",\"lowTime\":1632236056745,\"marketCap\":397369859400,\"oddLotDelayedPrice\":433.67,\"oddLotDelayedPriceTime\":1632254396989,\"open\":436.63,\"openTime\":1632231000366,\"openSource\":\"official\",\"peRatio\":null,\"previousClose\":434.04,\"previousVolume\":166445534,\"primaryExchange\":\"NYSE ARCA\",\"symbol\":\"SPY\",\"volume\":91339993,\"week52High\":452.6,\"wee" http_version: recorded_at: Tue, 21 Sep 2021 20:29:16 GMT recorded_with: VCR 5.1.0 diff --git a/spec/iex/endpoints/quote_spec.rb b/spec/iex/endpoints/quote_spec.rb index 8490d64..7daaaef 100644 --- a/spec/iex/endpoints/quote_spec.rb +++ b/spec/iex/endpoints/quote_spec.rb @@ -48,12 +48,15 @@ quotes << quote end - quotes.first + quotes end + let(:quote) { subject.last } + it 'retrieves a quote', vcr: { cassette_name: 'stream_quote/spy' } do - expect(subject.symbol).to eq('SPY') - expect(subject.close).to eq(433.63) + expect(subject.size).to eq(2) + expect(quote.symbol).to eq('SPY') + expect(quote.close).to eq(433.63) end end end From 88142b88f3221e977a219d17ef3f918ed6191908 Mon Sep 17 00:00:00 2001 From: Bogdan Guban Date: Wed, 22 Sep 2021 15:30:54 +0300 Subject: [PATCH 3/3] fix README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index adece4f..5ac5abb 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ A Ruby client for the [The IEX Cloud API](https://iexcloud.io/docs/api/). - [Configure](#configure) - [Get a Single Price](#get-a-single-price) - [Get a Quote](#get-a-quote) + - [Stream quotes](#stream-quotes) - [Get a OHLC (Open, High, Low, Close) price](#get-a-ohlc-open-high-low-close-price) - [Get a Market OHLC (Open, High, Low, Close) prices](#get-a-market-ohlc-open-high-low-close-prices) - [Get Historical Prices](#get-historical-prices)