stock-markit 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/stock-markit.rb +50 -0
- data/lib/stock-markit/api_exception.rb +22 -0
- data/lib/stock-markit/chart.rb +159 -0
- data/lib/stock-markit/chart_result.rb +47 -0
- data/lib/stock-markit/element.rb +37 -0
- data/lib/stock-markit/lookup.rb +52 -0
- data/lib/stock-markit/quote.rb +95 -0
- data/lib/stock-markit/stock.rb +26 -0
- data/lib/stock-markit/version.rb +9 -0
- data/spec/lib/stock-markit/lookup_spec.rb +35 -0
- data/spec/lib/stock-markit/quote_spec.rb +49 -0
- data/spec/lib/stock-markit/version_spec.rb +7 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/vcr.rb +6 -0
- data/spec/vcr/lookup/twtr.yml +50 -0
- data/spec/vcr/quote/twtr.yml +50 -0
- data/spec/vcr/quote/twtr_update.yml +97 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 41eb5980e6189fac8d4739c449b2eb2ac5a82401
|
4
|
+
data.tar.gz: 12d67a6a27900fe624b553e7d02b06b070c128e8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: da557ef3702ad11d5cb3bb9cc1029e22bcc44e3c5dd335483543f2737f5e8478bc3e29ac2e68dd7b13462feb2d83f3200ffcf898d7c9a9be8f13cd10a23a0b15
|
7
|
+
data.tar.gz: 547aa5837ec339ad15ba87026722c362b216d683b69cf98cb70ab1615bc3abe2f39f2c8df2c33388aaedbed7768581f6139f17ed3b2051bd29a88d067fa71db5
|
data/lib/stock-markit.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# This library allows you to persist objects with meta data
|
2
|
+
# StockMarkit supports data expiry and is designed with thread safety
|
3
|
+
#
|
4
|
+
# Author:: Michael Heijmans (mailto:parabuzzle@gmail.com)
|
5
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
6
|
+
# License:: MIT
|
7
|
+
|
8
|
+
require 'httparty'
|
9
|
+
require 'oj'
|
10
|
+
|
11
|
+
module StockMarkit
|
12
|
+
|
13
|
+
# require library file that was passed
|
14
|
+
# @param [String] lib Library path to require
|
15
|
+
def self.require_lib(lib)
|
16
|
+
require lib
|
17
|
+
end
|
18
|
+
|
19
|
+
# Iterates through the passed in array of
|
20
|
+
# library paths and requires each of them
|
21
|
+
# @param [Array] libs Array of libraries to require
|
22
|
+
def self.require_libs(libs)
|
23
|
+
libs.each do |lib|
|
24
|
+
self.require_lib(lib)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Uses the lookup service to find stocks with the given symbol
|
29
|
+
# @param [String, Symbol] symbol The ticker symbol to lookup
|
30
|
+
# @return [Array<StockMarkit::Stock>] An Array of Stock Objects that match the given symbol
|
31
|
+
# @see StockMarkit::Lookup
|
32
|
+
def self.lookup(symbol)
|
33
|
+
Lookup.new(symbol).fetch
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# Uses the quote service to get a quote for the given symbol
|
38
|
+
# @param [String, Symbol] symbol The ticker symbol to lookup
|
39
|
+
# @return [StockMarkit::Quote] A populated quote object
|
40
|
+
# @see StockMarkit::Quote
|
41
|
+
def self.quote(symbol)
|
42
|
+
Quote.new(symbol).fetch
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
$:.concat [File.expand_path('../', __FILE__),File.expand_path('../stock-markit', __FILE__)]
|
48
|
+
|
49
|
+
# Require all ruby files in the stock-markit directory
|
50
|
+
StockMarkit.require_libs Dir.glob(File.expand_path('../stock-markit', __FILE__) + '/*.rb')
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module StockMarkit
|
2
|
+
|
3
|
+
# Api Exception Object
|
4
|
+
#
|
5
|
+
# @attr_reader [String] message The Error Message
|
6
|
+
# @attr_reader [String] api_results The httparty object for inspecting
|
7
|
+
#
|
8
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
9
|
+
#
|
10
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
11
|
+
# License:: MIT
|
12
|
+
class ApiException < Exception
|
13
|
+
|
14
|
+
attr_reader :api_results
|
15
|
+
|
16
|
+
def initialize(message="Api Error", api_results=nil)
|
17
|
+
@api_results = api_results
|
18
|
+
super(message)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'active_support/time'
|
2
|
+
|
3
|
+
module StockMarkit
|
4
|
+
|
5
|
+
# Stock Chart Object
|
6
|
+
#
|
7
|
+
# @attr_reader [StockMarkit::ChartResult] results The chart results
|
8
|
+
#
|
9
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
10
|
+
#
|
11
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
12
|
+
# License:: MIT
|
13
|
+
class Chart
|
14
|
+
include ::HTTParty
|
15
|
+
base_uri 'dev.markitondemand.com'
|
16
|
+
|
17
|
+
attr_reader :results
|
18
|
+
|
19
|
+
# @option opts [Boolean] :normalized Show data in price units (false) or percentages (true)
|
20
|
+
# @option opts [Time] :start_date The beginning date for the chart
|
21
|
+
# @option opts [Time] :end_date The end date of the chart
|
22
|
+
# @option opts [Integer] :offset Number of days back that chart should end. Defaults to 0 if not specified. May be used instead of :end_date for interday requests.
|
23
|
+
# @option opts [Integer] :number_of_days Number of days that should be shown on the chart. Required for intraday requests. May be used instead of :start_date for interday requests.
|
24
|
+
# @option opts [Symbol] :data_period The type of data requested. :Minute, :Hour, :Day, :Week, :Month, :Quarter, :Year
|
25
|
+
# @option opts [Integer] :data_interval For intraday data, specifies the number of periods between data points. e.g. if DataPeriod is Minute and DataInterval is 5, you will get a chart with five minute intervals. Must be 0 or null for interday charts
|
26
|
+
# @option opts [Symbol] :label_period The TimePeriod over which to create labels. Control how often you want labels by setting LabelInterval. :Minute, :Hour, :Day, :Week, :Month, :Quarter, :Year
|
27
|
+
# @option opts [Integer] :label_interval How many LabelPeriods to skip between labels
|
28
|
+
# @option opts [Array<StockMarkit::Element>] :elements An Array of 1 or more Elements.
|
29
|
+
def initialize(opts)
|
30
|
+
@opts = opts
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean] value of the passed normalized parameter on instantiation
|
34
|
+
def normalized?
|
35
|
+
return true if @opts[:normalized].nil?
|
36
|
+
@opts[:normalized]
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] value of passed start_time parameter in ISO8601 formatted Eastern Time
|
40
|
+
def start_date
|
41
|
+
format_time(@opts[:start_date])
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [String] value of passed end_time parameter in ISO8601 formatted Eastern Time
|
45
|
+
def end_date
|
46
|
+
format_time(@opts[:end_date])
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Integer] value of passed offset parameter on instantiation
|
50
|
+
def offset
|
51
|
+
@opts[:offset]
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Integer] value of passed number_of_days parameter on instantiation
|
55
|
+
def number_of_days
|
56
|
+
@opts[:number_of_days]
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Symbol] value of passed data_period parameter on instantiation
|
60
|
+
def data_period
|
61
|
+
raise "valid data_periods are #{allowed_periods.join(", ")}" unless allowed_periods.include? @opts[:data_period]
|
62
|
+
@opts[:data_period].to_s.capitalize
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Integer] value of passed data_interval parameter on instantiation
|
66
|
+
def data_interval
|
67
|
+
@opts[:data_interval]
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Symbol] value of passed label_period parameter on instantiation
|
71
|
+
def label_period
|
72
|
+
return nil unless @opts[:label_period]
|
73
|
+
raise "valid label_periods are #{allowed_periods.join(", ")}" unless allowed_periods.include? @opts[:label_period]
|
74
|
+
@opts[:label_period].to_s.capitalize
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Integer] value of passed label_interval parameter on instantiation
|
78
|
+
def label_interval
|
79
|
+
@opts[:label_interval]
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Array] List of normalized elements passed on instantiation
|
83
|
+
def elements
|
84
|
+
@opts[:elements].map{ |element| {"Symbol" => element.symbol, "Type" => element.type, "Params" => [element.params]} }
|
85
|
+
end
|
86
|
+
|
87
|
+
# loads the @results on first call or returns results on subsequent calls
|
88
|
+
#
|
89
|
+
# @return [StockMarkit::ChartResults] results object
|
90
|
+
def fetch
|
91
|
+
@results || update
|
92
|
+
end
|
93
|
+
|
94
|
+
# updates the data from the api
|
95
|
+
#
|
96
|
+
# @return [StockMarkit::ChartResult] results object
|
97
|
+
def update
|
98
|
+
@results = lookup_with_api
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def allowed_periods
|
104
|
+
[
|
105
|
+
:minute,
|
106
|
+
:hour,
|
107
|
+
:day,
|
108
|
+
:week,
|
109
|
+
:month,
|
110
|
+
:quarter,
|
111
|
+
:year,
|
112
|
+
]
|
113
|
+
end
|
114
|
+
|
115
|
+
def format_time(time)
|
116
|
+
return nil unless time
|
117
|
+
time.in_time_zone('Eastern Time (US & Canada)').iso8601
|
118
|
+
end
|
119
|
+
|
120
|
+
def options
|
121
|
+
{
|
122
|
+
query: {
|
123
|
+
parameters: encoded_parameters
|
124
|
+
}
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def encoded_parameters
|
129
|
+
parameters.to_json
|
130
|
+
end
|
131
|
+
|
132
|
+
def parameters
|
133
|
+
params = {}
|
134
|
+
|
135
|
+
params.store("Normalized", normalized?)
|
136
|
+
params.store("StartDate", start_date) if start_date
|
137
|
+
params.store("EndDate", end_date) if end_date
|
138
|
+
params.store("EndOffsetDays", offset) if offset
|
139
|
+
params.store("NumberOfDays", number_of_days) if number_of_days
|
140
|
+
params.store("DataPeriod", data_period) if data_period
|
141
|
+
params.store("DataInterval", data_interval) if data_interval
|
142
|
+
params.store("LabelPeriod", label_period) if label_period
|
143
|
+
params.store("LabelInterval", label_interval) if label_interval
|
144
|
+
|
145
|
+
params.store("Elements", elements)
|
146
|
+
|
147
|
+
return params
|
148
|
+
end
|
149
|
+
|
150
|
+
def lookup_with_api
|
151
|
+
results = self.class.get("/MODApis/Api/v2/InteractiveChart/json", options)
|
152
|
+
if results.code != 200
|
153
|
+
raise ApiException.new("An error occured while attempting to communicate with the api", results)
|
154
|
+
end
|
155
|
+
StockMarkit::ChartResult.new( Oj.load( results.body ) )
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'active_support/time'
|
2
|
+
|
3
|
+
module StockMarkit
|
4
|
+
|
5
|
+
# Stock Chart Result Object
|
6
|
+
#
|
7
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
8
|
+
#
|
9
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
10
|
+
# License:: MIT
|
11
|
+
class ChartResult
|
12
|
+
|
13
|
+
# @param [Hash] the parsed json result from the chart api
|
14
|
+
def initialize(json)
|
15
|
+
@json = json
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Hash] X Axis label position, text, and dates. The "dates" are in Microsoft "OA Date" format. The "text" is an ISO timestamp.
|
19
|
+
def labels
|
20
|
+
@json["Labels"]
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Array] List of X coordinate positions for each data point returned, between 0 and 1.
|
24
|
+
def positions
|
25
|
+
@json["Positions"]
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Array] Timestamps corresponding to each position in UTC
|
29
|
+
def dates
|
30
|
+
@json["Dates"].map { |date| parse_date(date) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Array] requested element data
|
34
|
+
def elements
|
35
|
+
@json["Elements"]
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def parse_date(date)
|
42
|
+
timezone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
|
43
|
+
timezone.parse(date).utc
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module StockMarkit
|
2
|
+
|
3
|
+
# Stock Chart Element Object
|
4
|
+
#
|
5
|
+
# @attr_reader [String] symbol The Stock Symbol
|
6
|
+
# @attr_reader [String] type The type of element. Must be one of :price, :volume, or :sma
|
7
|
+
# @attr_reader [String] params Params vary for each Type. The following Types accept Params. For the other types, Params should be null or an empty array. "sma": [period], "price": ["ohlc"] for open/high/low/close, ["c"] for close only.
|
8
|
+
#
|
9
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
10
|
+
#
|
11
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
12
|
+
# License:: MIT
|
13
|
+
class Element
|
14
|
+
attr_reader :symbol, :type, :params
|
15
|
+
|
16
|
+
# @param [String] symbol The stock's ticker symbol
|
17
|
+
# @param [Symbol] type The type of element. Must be one of :price, :volume, or :sma
|
18
|
+
# @param [Array] params Params vary for each Type. The following Types accept Params. For the other types, Params should be null or an empty array. "sma": [period], "price": ["ohlc"] for open/high/low/close, ["c"] for close only.
|
19
|
+
def initialize(symbol, type, params=nil)
|
20
|
+
@symbol = symbol.to_s.upcase
|
21
|
+
@type = type.to_s
|
22
|
+
@params = params || default_params
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def default_params
|
28
|
+
case @type
|
29
|
+
when "price"
|
30
|
+
"c"
|
31
|
+
when "sma"
|
32
|
+
:week
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module StockMarkit
|
2
|
+
|
3
|
+
# Lookup a Stock by Symbol
|
4
|
+
#
|
5
|
+
# @attr_reader [String,Symbol] symbol The symbol of the stock to lookup
|
6
|
+
# @attr_reader [Hash] options Options hash for httparty
|
7
|
+
# @attr_reader [Array<StockMarkit::Stock>] results The stocks that match the symbol. This is populated on the first call of <#fetch>
|
8
|
+
#
|
9
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
10
|
+
#
|
11
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
12
|
+
# License:: MIT
|
13
|
+
class Lookup
|
14
|
+
include ::HTTParty
|
15
|
+
base_uri 'dev.markitondemand.com'
|
16
|
+
|
17
|
+
attr_reader :symbol, :results
|
18
|
+
|
19
|
+
# @param [String, Symbol] symbol The stock's ticker symbol
|
20
|
+
def initialize(symbol)
|
21
|
+
@symbol = symbol.to_sym.upcase
|
22
|
+
@options = { query: {input: @symbol} }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Fetch stocks matching @symbol from the api
|
26
|
+
#
|
27
|
+
# This method memoizes the results and returns the contents
|
28
|
+
# of the results variable instead of asking the api again
|
29
|
+
# @return [Array<StockMarkit::Stock>]
|
30
|
+
def fetch
|
31
|
+
@results ||= lookup_with_api
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def lookup_with_api
|
37
|
+
results = self.class.get("/MODApis/Api/v2/Lookup/json", @options)
|
38
|
+
unless results.code == 200
|
39
|
+
raise ApiException.new("An error occured while attempting to communicate with the api", results)
|
40
|
+
end
|
41
|
+
map_stocks( Oj.load( results.body ) )
|
42
|
+
end
|
43
|
+
|
44
|
+
def map_stocks(stocks)
|
45
|
+
stocks.map do |stock|
|
46
|
+
Stock.new(stock["Symbol"], stock['Name'], stock["Exchange"])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'active_support/time'
|
2
|
+
|
3
|
+
module StockMarkit
|
4
|
+
|
5
|
+
# Stock Quote
|
6
|
+
#
|
7
|
+
# @attr_reader [String] status The status from the api call
|
8
|
+
# @attr_reader [String] name The company name
|
9
|
+
# @attr_reader [String, Symbol] symbol The ticker symbol of the company
|
10
|
+
# @attr_reader [Float] last_price The last price of the company's stock
|
11
|
+
# @attr_reader [Float] change The change in price of the company's stock since the previous trading day's close
|
12
|
+
# @attr_reader [Float] change_percent The change percent in price of the company's stock since the previous trading day's close
|
13
|
+
# @attr_reader [Time] timestamp The last time the company's stock was traded
|
14
|
+
# @attr_reader [Float] ms_date The last time the company's stock was traded in exchange-local timezone. Represented as an OLE Automation date.
|
15
|
+
# @attr_reader [Integer] market_cap The company's market cap
|
16
|
+
# @attr_reader [Integer] volume The trade volume of the company's stock
|
17
|
+
# @attr_reader [Float] change_ytd The change in price of the company's stock since the start of the year
|
18
|
+
# @attr_reader [Float] change_percent_ytd The change percent in price of the company's stock since the start of the year
|
19
|
+
# @attr_reader [Float] high The high price of the company's stock in the trading session
|
20
|
+
# @attr_reader [Float] low The low price of the company's stock in the trading session
|
21
|
+
# @attr_reader [Float] open The opening price of the company's stock at the start of the trading session
|
22
|
+
#
|
23
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
24
|
+
#
|
25
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
26
|
+
# License:: MIT
|
27
|
+
class Quote
|
28
|
+
include ::HTTParty
|
29
|
+
base_uri 'dev.markitondemand.com'
|
30
|
+
|
31
|
+
attr_reader :status, :name, :symbol, :last_price,
|
32
|
+
:change, :change_percent, :timestamp,
|
33
|
+
:ms_date, :market_cap, :volume,
|
34
|
+
:change_ytd, :change_percent_ytd,
|
35
|
+
:high, :low, :open
|
36
|
+
|
37
|
+
# @param [String, Symbol] symbol The stock's ticker symbol
|
38
|
+
def initialize(symbol)
|
39
|
+
@symbol = symbol.to_sym.upcase
|
40
|
+
@options = { query: {symbol: @symbol} }
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return <self> on successful api call
|
44
|
+
# @return <False> on failed api call - check #message for failure message
|
45
|
+
# @see #update
|
46
|
+
def fetch
|
47
|
+
update
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return <self> on successful api call
|
51
|
+
# @return <False> on failed api call - check #message for failure message
|
52
|
+
# @see #fetch
|
53
|
+
def update
|
54
|
+
lookup_with_api
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def lookup_with_api
|
60
|
+
results = self.class.get("/MODApis/Api/v2/Quote/json", @options)
|
61
|
+
unless results.code == 200
|
62
|
+
raise ApiException.new("An error occured while attempting to communicate with the api", results)
|
63
|
+
end
|
64
|
+
map_stock( Oj.load( results.body ) )
|
65
|
+
end
|
66
|
+
|
67
|
+
def map_stock(stock)
|
68
|
+
if stock["Message"]
|
69
|
+
@status = stock["Message"]
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
@status = stock["Status"]
|
73
|
+
@name = stock["Name"]
|
74
|
+
@last_price = stock["LastPrice"]
|
75
|
+
@change = stock["Change"]
|
76
|
+
@change_percent = stock["ChangePercent"]
|
77
|
+
@timestamp = parse_time(stock["Timestamp"])
|
78
|
+
@ms_date = stock["MSDate"]
|
79
|
+
@market_cap = stock["MarketCap"]
|
80
|
+
@volume = stock["Volume"]
|
81
|
+
@change_ytd = stock["ChangeYTD"]
|
82
|
+
@change_percent_ytd = stock["ChangePercentYTD"]
|
83
|
+
@high = stock["High"]
|
84
|
+
@low = stock["Low"]
|
85
|
+
@open = stock["Open"]
|
86
|
+
return self
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_time(stamp)
|
90
|
+
timezone = ActiveSupport::TimeZone["Eastern Time (US & Canada)"]
|
91
|
+
timezone.parse(stamp).utc
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module StockMarkit
|
2
|
+
|
3
|
+
# Stock Object
|
4
|
+
#
|
5
|
+
# @attr_reader [String] symbol The Stock Symbol
|
6
|
+
# @attr_reader [String] name The company name
|
7
|
+
# @attr_reader [String] exchange The exchange the stock is traded on
|
8
|
+
#
|
9
|
+
# @author Michael Heijmans (mailto:parabuzzle@gmail.com)
|
10
|
+
#
|
11
|
+
# Copyright:: Copyright (c) 2016 Michael Heijmans
|
12
|
+
# License:: MIT
|
13
|
+
class Stock
|
14
|
+
attr_reader :symbol, :name, :exchange
|
15
|
+
|
16
|
+
# @param [String] symbol The stock's ticker symbol
|
17
|
+
# @param [String] name The name of the company
|
18
|
+
# @param [String] exchange Optional exchange string
|
19
|
+
def initialize(symbol, name, exchange=nil)
|
20
|
+
@symbol = symbol
|
21
|
+
@name = name
|
22
|
+
@exchange = exchange
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'StockMarkit::Lookup' do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'sets the instance symbol as an upcased symbol' do
|
6
|
+
lookup = StockMarkit::Lookup.new(:twtr)
|
7
|
+
expect( lookup.symbol ).to eq(:TWTR)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'accepts a string and converts to symbol internally' do
|
11
|
+
lookup = StockMarkit::Lookup.new('twtr')
|
12
|
+
expect( lookup.symbol ).to eq(:TWTR)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#fetch' do
|
17
|
+
it 'fetches the stock data from the api' do
|
18
|
+
VCR.use_cassette 'lookup/twtr' do
|
19
|
+
lookup = StockMarkit::Lookup.new(:twtr)
|
20
|
+
lookup.fetch
|
21
|
+
expect( lookup.results ).to be_a(Array)
|
22
|
+
expect( lookup.results.first ).to be_a(StockMarkit::Stock)
|
23
|
+
expect( lookup.results.first.name ).to eq("Twitter Inc")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'memoizes the results' do
|
28
|
+
VCR.use_cassette 'lookup/twtr' do
|
29
|
+
lookup = StockMarkit::Lookup.new(:twtr)
|
30
|
+
results = lookup.fetch
|
31
|
+
expect( lookup.fetch.equal?(results) ).to eq(true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'StockMarkit::Quote' do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'sets the instance symbol as an upcased symbol' do
|
6
|
+
quote = StockMarkit::Quote.new(:twtr)
|
7
|
+
expect( quote.symbol ).to eq(:TWTR)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'accepts a string and converts to symbol internally' do
|
11
|
+
quote = StockMarkit::Quote.new('twtr')
|
12
|
+
expect( quote.symbol ).to eq(:TWTR)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#fetch' do
|
17
|
+
let(:quote) {StockMarkit::Quote.new(:twtr)}
|
18
|
+
|
19
|
+
it 'fetches the stock quote data from the api' do
|
20
|
+
VCR.use_cassette 'quote/twtr' do
|
21
|
+
expect( quote.name ).to be_nil
|
22
|
+
quote.fetch
|
23
|
+
expect( quote.name ).not_to be_nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'sets the timestamp as a utc time' do
|
28
|
+
VCR.use_cassette 'quote/twtr' do
|
29
|
+
quote.fetch
|
30
|
+
expect( quote.timestamp.zone ).to eq('UTC')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#update' do
|
36
|
+
let(:quote) {StockMarkit::Quote.new(:twtr)}
|
37
|
+
|
38
|
+
it 'updates quote from the api' do
|
39
|
+
VCR.use_cassette 'quote/twtr_update' do
|
40
|
+
quote.fetch
|
41
|
+
expect( quote.name ).not_to be_nil
|
42
|
+
quote.instance_eval('@name = nil')
|
43
|
+
expect( quote.name ).to be_nil
|
44
|
+
quote.update
|
45
|
+
expect( quote.name ).not_to be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# This file is loaded before rspec tests are run
|
2
|
+
require 'simplecov'
|
3
|
+
require 'simplecov-rcov'
|
4
|
+
require 'coveralls'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
require_relative './support/vcr.rb'
|
8
|
+
|
9
|
+
require 'stock-markit'
|
10
|
+
|
11
|
+
SimpleCov.start do
|
12
|
+
add_filter '/spec/'
|
13
|
+
add_group 'lib', 'lib'
|
14
|
+
#SimpleCov.minimum_coverage 75
|
15
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
16
|
+
SimpleCov::Formatter::HTMLFormatter,
|
17
|
+
SimpleCov::Formatter::RcovFormatter
|
18
|
+
]
|
19
|
+
end if ENV["COVERAGE"]
|
20
|
+
|
21
|
+
Coveralls.wear!
|
data/spec/support/vcr.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://dev.markitondemand.com/MODApis/Api/v2/Lookup/json?input=TWTR
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- "*/*"
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Cache-Control:
|
22
|
+
- private
|
23
|
+
Content-Type:
|
24
|
+
- text/javascript; charset=UTF-8
|
25
|
+
Vary:
|
26
|
+
- Accept-Encoding
|
27
|
+
Server:
|
28
|
+
- ''
|
29
|
+
X-Aspnet-Version:
|
30
|
+
- ''
|
31
|
+
P3p:
|
32
|
+
- CP="NON PHY ONL UNI PUR FIN COM NAV INT DEM STA HEA CUR ADM DEV OUR IND",
|
33
|
+
policyref="/w3c/p3p.xml"
|
34
|
+
Set-Cookie:
|
35
|
+
- 3481%5F0=F4818F874E6B510347577EE44860372EBD4802692B02483A4BDC4E27ACCC5F05;
|
36
|
+
path=/; HttpOnly
|
37
|
+
- GZIP=1; expires=Sat, 04-Sep-2021 18:58:55 GMT; path=/
|
38
|
+
X-Powered-By:
|
39
|
+
- ASP.NET
|
40
|
+
Date:
|
41
|
+
- Mon, 05 Sep 2016 18:58:55 GMT
|
42
|
+
Content-Length:
|
43
|
+
- '191'
|
44
|
+
body:
|
45
|
+
encoding: ASCII-8BIT
|
46
|
+
string: '[{"Symbol":"TWTR","Name":"Twitter Inc","Exchange":"NYSE"},{"Symbol":"TWTR","Name":"Twitter
|
47
|
+
Inc","Exchange":"BATS Trading Inc"}]'
|
48
|
+
http_version:
|
49
|
+
recorded_at: Mon, 05 Sep 2016 18:58:55 GMT
|
50
|
+
recorded_with: VCR 3.0.3
|
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://dev.markitondemand.com/MODApis/Api/v2/Quote/json?symbol=TWTR
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- "*/*"
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Cache-Control:
|
22
|
+
- private
|
23
|
+
Content-Type:
|
24
|
+
- text/javascript; charset=UTF-8
|
25
|
+
Vary:
|
26
|
+
- Accept-Encoding
|
27
|
+
Server:
|
28
|
+
- ''
|
29
|
+
X-Aspnet-Version:
|
30
|
+
- ''
|
31
|
+
P3p:
|
32
|
+
- CP="NON PHY ONL UNI PUR FIN COM NAV INT DEM STA HEA CUR ADM DEV OUR IND",
|
33
|
+
policyref="/w3c/p3p.xml"
|
34
|
+
Set-Cookie:
|
35
|
+
- 3481%5F0=73B343DA043278F396CBECA8FB707A1EA9971FED219369C125A5957958B2C927;
|
36
|
+
path=/; HttpOnly
|
37
|
+
- GZIP=1; expires=Sat, 04-Sep-2021 19:10:49 GMT; path=/
|
38
|
+
X-Powered-By:
|
39
|
+
- ASP.NET
|
40
|
+
Date:
|
41
|
+
- Mon, 05 Sep 2016 19:10:48 GMT
|
42
|
+
Content-Length:
|
43
|
+
- '378'
|
44
|
+
body:
|
45
|
+
encoding: ASCII-8BIT
|
46
|
+
string: '{"Status":"SUCCESS","Name":"Twitter Inc","Symbol":"TWTR","LastPrice":19.54,"Change":0.0399999999999991,"ChangePercent":0.205128205128201,"Timestamp":"Fri
|
47
|
+
Sep 2 15:59:00 UTC-04:00 2016","MSDate":42615.6659722222,"MarketCap":13828985580,"Volume":949554,"ChangeYTD":23.14,"ChangePercentYTD":-15.5574762316335,"High":19.87,"Low":19.355,"Open":19.61}'
|
48
|
+
http_version:
|
49
|
+
recorded_at: Mon, 05 Sep 2016 19:10:49 GMT
|
50
|
+
recorded_with: VCR 3.0.3
|
@@ -0,0 +1,97 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://dev.markitondemand.com/MODApis/Api/v2/Quote/json?symbol=TWTR
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept-Encoding:
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
12
|
+
Accept:
|
13
|
+
- "*/*"
|
14
|
+
User-Agent:
|
15
|
+
- Ruby
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Cache-Control:
|
22
|
+
- private
|
23
|
+
Content-Type:
|
24
|
+
- text/javascript; charset=UTF-8
|
25
|
+
Vary:
|
26
|
+
- Accept-Encoding
|
27
|
+
Server:
|
28
|
+
- ''
|
29
|
+
X-Aspnet-Version:
|
30
|
+
- ''
|
31
|
+
P3p:
|
32
|
+
- CP="NON PHY ONL UNI PUR FIN COM NAV INT DEM STA HEA CUR ADM DEV OUR IND",
|
33
|
+
policyref="/w3c/p3p.xml"
|
34
|
+
Set-Cookie:
|
35
|
+
- 3481%5F0=3341EF7F0399B17CB017F9EBD7F5F1BD5091D0BC6FB4A567CA792985822FFD10;
|
36
|
+
path=/; HttpOnly
|
37
|
+
- GZIP=1; expires=Sat, 04-Sep-2021 19:18:44 GMT; path=/
|
38
|
+
X-Powered-By:
|
39
|
+
- ASP.NET
|
40
|
+
Date:
|
41
|
+
- Mon, 05 Sep 2016 19:18:43 GMT
|
42
|
+
Content-Length:
|
43
|
+
- '378'
|
44
|
+
body:
|
45
|
+
encoding: ASCII-8BIT
|
46
|
+
string: '{"Status":"SUCCESS","Name":"Twitter Inc","Symbol":"TWTR","LastPrice":19.54,"Change":0.0399999999999991,"ChangePercent":0.205128205128201,"Timestamp":"Fri
|
47
|
+
Sep 2 15:59:00 UTC-04:00 2016","MSDate":42615.6659722222,"MarketCap":13828985580,"Volume":949554,"ChangeYTD":23.14,"ChangePercentYTD":-15.5574762316335,"High":19.87,"Low":19.355,"Open":19.61}'
|
48
|
+
http_version:
|
49
|
+
recorded_at: Mon, 05 Sep 2016 19:18:44 GMT
|
50
|
+
- request:
|
51
|
+
method: get
|
52
|
+
uri: http://dev.markitondemand.com/MODApis/Api/v2/Quote/json?symbol=TWTR
|
53
|
+
body:
|
54
|
+
encoding: US-ASCII
|
55
|
+
string: ''
|
56
|
+
headers:
|
57
|
+
Accept-Encoding:
|
58
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
59
|
+
Accept:
|
60
|
+
- "*/*"
|
61
|
+
User-Agent:
|
62
|
+
- Ruby
|
63
|
+
response:
|
64
|
+
status:
|
65
|
+
code: 200
|
66
|
+
message: OK
|
67
|
+
headers:
|
68
|
+
Cache-Control:
|
69
|
+
- private
|
70
|
+
Content-Type:
|
71
|
+
- text/javascript; charset=UTF-8
|
72
|
+
Vary:
|
73
|
+
- Accept-Encoding
|
74
|
+
Server:
|
75
|
+
- ''
|
76
|
+
X-Aspnet-Version:
|
77
|
+
- ''
|
78
|
+
P3p:
|
79
|
+
- CP="NON PHY ONL UNI PUR FIN COM NAV INT DEM STA HEA CUR ADM DEV OUR IND",
|
80
|
+
policyref="/w3c/p3p.xml"
|
81
|
+
Set-Cookie:
|
82
|
+
- 3481%5F0=C4D853D2672331B8914DC4512337700963C25A602B0C546F69C622EF7562EA04;
|
83
|
+
path=/; HttpOnly
|
84
|
+
- GZIP=1; expires=Sat, 04-Sep-2021 19:18:44 GMT; path=/
|
85
|
+
X-Powered-By:
|
86
|
+
- ASP.NET
|
87
|
+
Date:
|
88
|
+
- Mon, 05 Sep 2016 19:18:43 GMT
|
89
|
+
Content-Length:
|
90
|
+
- '378'
|
91
|
+
body:
|
92
|
+
encoding: ASCII-8BIT
|
93
|
+
string: '{"Status":"SUCCESS","Name":"Twitter Inc","Symbol":"TWTR","LastPrice":19.54,"Change":0.0399999999999991,"ChangePercent":0.205128205128201,"Timestamp":"Fri
|
94
|
+
Sep 2 15:59:00 UTC-04:00 2016","MSDate":42615.6659722222,"MarketCap":13828985580,"Volume":949554,"ChangeYTD":23.14,"ChangePercentYTD":-15.5574762316335,"High":19.87,"Low":19.355,"Open":19.61}'
|
95
|
+
http_version:
|
96
|
+
recorded_at: Mon, 05 Sep 2016 19:18:44 GMT
|
97
|
+
recorded_with: VCR 3.0.3
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stock-markit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Heijmans
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.14'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oj
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.17'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.17'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
description: the stock-markit gem brings all the great information from Markit on
|
70
|
+
Demand to your ruby project (http://dev.markitondemand.com/)
|
71
|
+
email: parabuzzle@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/stock-markit.rb
|
77
|
+
- lib/stock-markit/api_exception.rb
|
78
|
+
- lib/stock-markit/chart.rb
|
79
|
+
- lib/stock-markit/chart_result.rb
|
80
|
+
- lib/stock-markit/element.rb
|
81
|
+
- lib/stock-markit/lookup.rb
|
82
|
+
- lib/stock-markit/quote.rb
|
83
|
+
- lib/stock-markit/stock.rb
|
84
|
+
- lib/stock-markit/version.rb
|
85
|
+
- spec/lib/stock-markit/lookup_spec.rb
|
86
|
+
- spec/lib/stock-markit/quote_spec.rb
|
87
|
+
- spec/lib/stock-markit/version_spec.rb
|
88
|
+
- spec/spec_helper.rb
|
89
|
+
- spec/support/vcr.rb
|
90
|
+
- spec/vcr/lookup/twtr.yml
|
91
|
+
- spec/vcr/quote/twtr.yml
|
92
|
+
- spec/vcr/quote/twtr_update.yml
|
93
|
+
homepage: https://github.com/parabuzzle/stock-markit
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 2.5.1
|
114
|
+
signing_key:
|
115
|
+
specification_version: 4
|
116
|
+
summary: stock-markit is the ruby interface for Markit on Demand.
|
117
|
+
test_files:
|
118
|
+
- spec/lib/stock-markit/lookup_spec.rb
|
119
|
+
- spec/lib/stock-markit/quote_spec.rb
|
120
|
+
- spec/lib/stock-markit/version_spec.rb
|
121
|
+
- spec/spec_helper.rb
|
122
|
+
- spec/support/vcr.rb
|
123
|
+
- spec/vcr/lookup/twtr.yml
|
124
|
+
- spec/vcr/quote/twtr.yml
|
125
|
+
- spec/vcr/quote/twtr_update.yml
|