options-lib 0.9.2
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.
- data/README.markdown +9 -0
- data/bin/show_quotes +35 -0
- data/lib/options-lib/helpers.rb +36 -0
- data/lib/options-lib/option.rb +62 -0
- data/lib/options-lib/option_quote.rb +29 -0
- data/lib/options-lib/yahoo_crawler.rb +169 -0
- data/lib/options-lib.rb +6 -0
- data/options-lib.gemspec +26 -0
- data/spec/helpers_spec.rb +46 -0
- data/spec/option_quote_spec.rb +23 -0
- data/spec/option_spec.rb +63 -0
- data/spec/yahoo_crawler_spec.rb +59 -0
- metadata +91 -0
data/README.markdown
ADDED
data/bin/show_quotes
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'options-lib'
|
5
|
+
rescue LoadError
|
6
|
+
require 'rubygems'
|
7
|
+
require 'options-lib'
|
8
|
+
end
|
9
|
+
|
10
|
+
if ARGV.length != 5
|
11
|
+
puts "format: show_quotes <stock> <call_or_put> <strike> <expiration> <reload_period>"
|
12
|
+
puts "example: show_quotes AAPL C 345 2013-01-18 5"
|
13
|
+
exit
|
14
|
+
end
|
15
|
+
|
16
|
+
stock = ARGV[0]
|
17
|
+
is_call = ARGV[1].casecmp("C") == 0
|
18
|
+
strike = ARGV[2].to_f
|
19
|
+
expiration = ARGV[3]
|
20
|
+
reload_period = ARGV[4].to_i
|
21
|
+
|
22
|
+
y = YahooCrawler.new(stock, expiration)
|
23
|
+
|
24
|
+
y.auto_reload(reload_period) do
|
25
|
+
|
26
|
+
options = is_call ? y.call_options : y.put_options
|
27
|
+
quote = options[strike]
|
28
|
+
stock_price = y.curr_stock_price
|
29
|
+
|
30
|
+
puts "Stock @ #{stock_price} Options: #{quote.bid} | #{quote.ask} Spread: #{quote.spread}"
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
y.join_reload_thread
|
35
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Float
|
2
|
+
|
3
|
+
# Supress decimal part if it is zero
|
4
|
+
# 40.0 becomes "40"
|
5
|
+
# Take care of float imprecision
|
6
|
+
# 1.1-0.9 # >> 0.20000000000000007
|
7
|
+
# (1.1-0.9).prettify # >> "0.2"
|
8
|
+
def prettify
|
9
|
+
num = "%.12g" % self
|
10
|
+
num.sub!($1, '') if num =~ /\..*?(0+)$/
|
11
|
+
# might be like 2. at this point
|
12
|
+
num = num[0..-2] if num[-1] == '.'
|
13
|
+
num
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Array
|
18
|
+
|
19
|
+
# Break the array into smaller arrays (chuncks)
|
20
|
+
# If the lenght of the array is not divisible by the number of chunks (pieces)
|
21
|
+
# then first chunk will accomodate the extra item and be the larger chunk
|
22
|
+
# Ex: [1,2,3,4].chunck # => [[1,2], [3,4]]
|
23
|
+
def chunk(pieces=2)
|
24
|
+
len = self.length;
|
25
|
+
mid = (len / pieces)
|
26
|
+
chunks = []
|
27
|
+
start = 0
|
28
|
+
1.upto(pieces) do |i|
|
29
|
+
last = start + mid
|
30
|
+
last = last - 1 unless len % pieces >= i
|
31
|
+
chunks << self[start..last] || []
|
32
|
+
start = last + 1
|
33
|
+
end
|
34
|
+
chunks
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'date'
|
2
|
+
require_relative 'helpers'
|
3
|
+
|
4
|
+
class Option
|
5
|
+
|
6
|
+
CALL = 'CALL'
|
7
|
+
PUT = 'PUT'
|
8
|
+
|
9
|
+
attr_reader :stock, :exp, :type, :strike, :symbol, :internal_symbol
|
10
|
+
|
11
|
+
def initialize(type, stock, strike, exp, symbol = nil)
|
12
|
+
raise "Invalid type: #{type}" if type != CALL and type != PUT
|
13
|
+
|
14
|
+
@type, @stock, @strike = type, stock, strike.to_f
|
15
|
+
|
16
|
+
if exp =~ /(\d{4})[\-\/](\d{1,2})[\-\/](\d{1,2})/
|
17
|
+
@exp = Date.new($1.to_i, $2.to_i, $3.to_i)
|
18
|
+
else
|
19
|
+
raise "Cannot parse expiration date: #{exp}"
|
20
|
+
end
|
21
|
+
|
22
|
+
s = "#{stock}_#{type == CALL ? 'C' : 'P'}#{@strike.prettify}_#{@exp.day}"
|
23
|
+
s << Date::MONTHNAMES[@exp.month][0,3].upcase
|
24
|
+
s << @exp.year.to_s
|
25
|
+
@internal_symbol = s
|
26
|
+
|
27
|
+
if not symbol
|
28
|
+
@symbol = @internal_symbol
|
29
|
+
else
|
30
|
+
@symbol = symbol
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def is_call?
|
35
|
+
@type == CALL
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_put?
|
39
|
+
@type == PUT
|
40
|
+
end
|
41
|
+
|
42
|
+
# Business days (= minus Sat and Sun) until expiration
|
43
|
+
def days_to_exp
|
44
|
+
total = 0
|
45
|
+
curr_day = Date.today
|
46
|
+
while curr_day <= exp
|
47
|
+
total += 1 if curr_day.wday != 0 and curr_day.wday != 6
|
48
|
+
curr_day = curr_day.next
|
49
|
+
end
|
50
|
+
total
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
@internal_symbol
|
55
|
+
end
|
56
|
+
|
57
|
+
def inspect
|
58
|
+
@internal_symbol
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'option'
|
2
|
+
|
3
|
+
class OptionQuote
|
4
|
+
|
5
|
+
attr_reader :option, :bid, :ask
|
6
|
+
|
7
|
+
def initialize(option, args)
|
8
|
+
@option = option
|
9
|
+
@bid = args[:bid] || nil
|
10
|
+
@ask = args[:ask] || nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def spread
|
14
|
+
if @bid and @ask
|
15
|
+
(@ask - @bid).prettify.to_f
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"#{option.to_s}: #{bid.inspect} / #{ask.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
"#{option.inspect}: bid => #{bid.inspect}, ask => #{ask.inspect}"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'Mechanize'
|
3
|
+
|
4
|
+
require_relative 'option'
|
5
|
+
require_relative 'option_quote'
|
6
|
+
require_relative 'helpers'
|
7
|
+
|
8
|
+
# A Yahoo!Finance crawler implemented using Mechanize to parse HTML and extract
|
9
|
+
# options quotes for given stock and expiration date.
|
10
|
+
class YahooCrawler
|
11
|
+
|
12
|
+
def initialize(stock, exp)
|
13
|
+
@mech = Mechanize.new
|
14
|
+
@stock, @exp = stock, exp
|
15
|
+
@url = "http://finance.yahoo.com/q/op?s=#{stock}&m=#{exp[0,7]}"
|
16
|
+
@stock_curr_price = nil
|
17
|
+
@call_options = Hash.new
|
18
|
+
@put_options = Hash.new
|
19
|
+
@call_strikes = Array.new
|
20
|
+
@put_strikes = Array.new
|
21
|
+
@lock = Mutex.new
|
22
|
+
@t_lock = Mutex.new
|
23
|
+
@thread = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def auto_reload(period = 60)
|
27
|
+
if not @thread.nil?
|
28
|
+
stop
|
29
|
+
end
|
30
|
+
|
31
|
+
@thread = Thread.new do
|
32
|
+
loop do
|
33
|
+
begin
|
34
|
+
fetch
|
35
|
+
yield # callback
|
36
|
+
rescue => e
|
37
|
+
puts "Error fetching data: #{e.message}"
|
38
|
+
end
|
39
|
+
sleep period
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop
|
46
|
+
if not @thread.nil?
|
47
|
+
@thread.kill
|
48
|
+
@thread = nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def join_reload_thread
|
53
|
+
@thread.join if @thread
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch
|
57
|
+
|
58
|
+
c_options, p_options = Hash.new, Hash.new
|
59
|
+
c_strikes, p_strikes = Array.new, Array.new
|
60
|
+
stock_price = nil
|
61
|
+
|
62
|
+
@t_lock.synchronize { # don't step into each other in case someone calls fetch
|
63
|
+
|
64
|
+
page = @mech.get(@url).body
|
65
|
+
|
66
|
+
curr_price = page.scan(/\: \<.+?\<\/big\>/)
|
67
|
+
lines = parse_data(curr_price[0])
|
68
|
+
stock_price = lines[0].to_f
|
69
|
+
|
70
|
+
calls = page.scan(/Strike\<.+Put Options/)
|
71
|
+
puts = page.scan(/Put Options\<.+Highlighted/)
|
72
|
+
|
73
|
+
lines = parse_data(calls[0])
|
74
|
+
lines = lines.chunk(lines.length / 8)
|
75
|
+
|
76
|
+
lines.each do |array|
|
77
|
+
quote = parse_quote(array, Option::CALL)
|
78
|
+
c_options[quote.option.strike] = quote
|
79
|
+
end
|
80
|
+
|
81
|
+
lines = parse_data(puts[0])
|
82
|
+
lines = lines.chunk(lines.length / 8)
|
83
|
+
|
84
|
+
lines.each do |array|
|
85
|
+
quote = parse_quote(array, Option::PUT)
|
86
|
+
p_options[quote.option.strike] = quote
|
87
|
+
end
|
88
|
+
|
89
|
+
c_options.keys.sort.each { |key| c_strikes << key }
|
90
|
+
|
91
|
+
p_options.keys.sort.each { |key| p_strikes << key }
|
92
|
+
|
93
|
+
}
|
94
|
+
|
95
|
+
@lock.synchronize {
|
96
|
+
@call_options, @put_options = c_options, p_options
|
97
|
+
@call_strikes, @put_strikes = c_strikes, p_strikes
|
98
|
+
@curr_stock_price = stock_price
|
99
|
+
}
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_option_quote(type, strike)
|
104
|
+
if type == Option::CALL
|
105
|
+
get_call_option_quote(strike)
|
106
|
+
else
|
107
|
+
get_put_option_quote(strike)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_call_option_quote(strike)
|
112
|
+
call_options[strike]
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_put_option_quote(strike)
|
116
|
+
put_options[strike]
|
117
|
+
end
|
118
|
+
|
119
|
+
def call_strikes
|
120
|
+
@lock.synchronize { @call_strikes }
|
121
|
+
end
|
122
|
+
|
123
|
+
def put_strikes
|
124
|
+
@lock.synchronize { @put_strikes }
|
125
|
+
end
|
126
|
+
|
127
|
+
def call_options
|
128
|
+
@lock.synchronize { @call_options }
|
129
|
+
end
|
130
|
+
|
131
|
+
def put_options
|
132
|
+
@lock.synchronize { @put_options }
|
133
|
+
end
|
134
|
+
|
135
|
+
def curr_stock_price
|
136
|
+
@lock.synchronize { @curr_stock_price }
|
137
|
+
end
|
138
|
+
|
139
|
+
def show_calls
|
140
|
+
call_strikes.each { |key| puts call_options[key] }
|
141
|
+
end
|
142
|
+
|
143
|
+
def show_puts
|
144
|
+
put_strikes.each { |key| puts put_options[key] }
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def parse_quote(line, type)
|
150
|
+
strike = line[0].to_f
|
151
|
+
symbol = line[1]
|
152
|
+
bid = f line[4]
|
153
|
+
ask = f line[5]
|
154
|
+
|
155
|
+
o = Option.new(type, @stock, strike, @exp, symbol)
|
156
|
+
OptionQuote.new(o, :bid => bid, :ask => ask)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Get all values inside tags
|
160
|
+
# Ex: <h1>234</h1> will return "234"
|
161
|
+
def parse_data(data)
|
162
|
+
data.scan(/\>[0-9\.A-Z\,\/]+\</).collect { |i| i.gsub(/[\<\>]/, "") }
|
163
|
+
end
|
164
|
+
|
165
|
+
def f(data)
|
166
|
+
data == 'N/A' ? nil : data.to_f
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
data/lib/options-lib.rb
ADDED
data/options-lib.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'options-lib'
|
3
|
+
s.version = '0.9.2'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.summary = 'Options Library'
|
6
|
+
s.description = 'A set of classes for dealing with options. It includes a crawler for Yahoo!Finance.'
|
7
|
+
|
8
|
+
s.author = 'Sergio Oliveira Jr.'
|
9
|
+
s.email = 'sergio.oliveira.jr@gmail.com'
|
10
|
+
s.homepage = 'https://github.com/saoj/options-lib'
|
11
|
+
|
12
|
+
# These dependencies are only for people who work on this gem
|
13
|
+
s.add_development_dependency 'rspec'
|
14
|
+
|
15
|
+
# Runtime dependencies
|
16
|
+
s.add_runtime_dependency 'mechanize'
|
17
|
+
|
18
|
+
# The list of files to be contained in the gem
|
19
|
+
s.files = `git ls-files`.split("\n") - [".gitignore"]
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
|
23
|
+
# Supress the warning about no rubyforge project
|
24
|
+
s.rubyforge_project = 'nowarning'
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# require all files from lib
|
2
|
+
Dir["./lib/*.rb"].each {|file| require file }
|
3
|
+
|
4
|
+
describe Float do
|
5
|
+
|
6
|
+
it 'can format its value in a pretty way' do
|
7
|
+
|
8
|
+
# if it has a decimal part, print it
|
9
|
+
f = 50.1
|
10
|
+
f.prettify.should == "50.1"
|
11
|
+
|
12
|
+
# if decimal part is 0, do not print it
|
13
|
+
f = 50.0
|
14
|
+
f.prettify.should == "50"
|
15
|
+
|
16
|
+
# take care of ruby float imprecision
|
17
|
+
f = 1.1 - 0.9
|
18
|
+
f.should_not == 0.2 # weird but true !!!
|
19
|
+
f.prettify.should == "0.2" # much better!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Array do
|
24
|
+
|
25
|
+
it 'can be broken into chunks of smaller arrays' do
|
26
|
+
|
27
|
+
# the first chunk is always the bigger in case the number of
|
28
|
+
# elements are not divisible by pieces
|
29
|
+
|
30
|
+
a = [1,2,3,4]
|
31
|
+
a.chunk.should == [[1,2],[3,4]]
|
32
|
+
a.chunk(3).should == [[1,2],[3],[4]]
|
33
|
+
a.chunk(4).should == [[1],[2],[3],[4]]
|
34
|
+
|
35
|
+
a << 5 << 6
|
36
|
+
a.chunk.should == [[1,2,3],[4,5,6]]
|
37
|
+
a.chunk(3).should == [[1,2],[3,4],[5,6]]
|
38
|
+
|
39
|
+
a << 7
|
40
|
+
a.chunk.should == [[1,2,3,4],[5,6,7]]
|
41
|
+
a.chunk(3).should == [[1,2,3],[4,5],[6,7]]
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# require all files from lib
|
2
|
+
Dir["./lib/*.rb"].each {|file| require file }
|
3
|
+
|
4
|
+
describe OptionQuote do
|
5
|
+
|
6
|
+
it 'has option, bid and ask prices' do
|
7
|
+
|
8
|
+
o = Option.new(Option::CALL, 'AAPL', 250, '2010-02-14')
|
9
|
+
q = OptionQuote.new(o, :bid => 1.2, :ask => 1.4)
|
10
|
+
q.option.should == o
|
11
|
+
q.bid.should == 1.2
|
12
|
+
q.ask.should == 1.4
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should have an spread' do
|
17
|
+
o = Option.new(Option::CALL, 'AAPL', 250, '2010-02-14')
|
18
|
+
q = OptionQuote.new(o, :bid => 1.2, :ask => 1.4)
|
19
|
+
q.spread.prettify.should == "0.2"
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/spec/option_spec.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# require all files from lib
|
2
|
+
Dir["./lib/*.rb"].each {|file| require file }
|
3
|
+
|
4
|
+
describe Option do
|
5
|
+
|
6
|
+
it 'can be a call or a put' do
|
7
|
+
o = Option.new(Option::CALL, 'AAPL', 250, '2010-02-14')
|
8
|
+
o.type.should == Option::CALL
|
9
|
+
o.type.should == 'CALL'
|
10
|
+
|
11
|
+
o = Option.new(Option::PUT, 'AAPL', 250, '2010-02-14')
|
12
|
+
o.type.should == Option::PUT
|
13
|
+
o.type.should == 'PUT'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has stock and strike price' do
|
17
|
+
o = Option.new(Option::CALL, 'AAPL', 250, '2010-02-14')
|
18
|
+
o.stock.should == 'AAPL'
|
19
|
+
o.strike.should == 250
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'converts expiration to a nice date object' do
|
24
|
+
o = Option.new(Option::PUT, 'AAPL', 250, '2010-08-14')
|
25
|
+
o.exp.day.should == 14
|
26
|
+
o.exp.month.should == 8
|
27
|
+
o.exp.year.should == 2010
|
28
|
+
|
29
|
+
o = Option.new(Option::CALL, 'AAPL', 250, '2011/09/15')
|
30
|
+
o.exp.day.should == 15
|
31
|
+
o.exp.month.should == 9
|
32
|
+
o.exp.year.should == 2011
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'throws an exception if date is bad' do
|
36
|
+
lambda {Option.new(Option::PUT, 'AAPL', 250, '10-08-14')}.should raise_error
|
37
|
+
lambda {Option.new(Option::PUT, 'AAPL', 250, '10-58-14')}.should raise_error
|
38
|
+
lambda {Option.new(Option::PUT, 'AAPL', 250, '2010-122-14')}.should raise_error
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'has an internal symbol representation implied from stock, strike and experiration' do
|
42
|
+
o = Option.new(Option::PUT, 'AAPL', 250, '2010-08-14')
|
43
|
+
o.internal_symbol.should == 'AAPL_P250_14AUG2010'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'makes the symbol equals to the internal symbol if symbol not provided' do
|
47
|
+
o = Option.new(Option::PUT, 'AAPL', 250, '2010-08-14')
|
48
|
+
o.internal_symbol.should == o.symbol
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can have a symbol' do
|
52
|
+
o = Option.new(Option::PUT, 'AAPL', 250, '2010-08-14', 'MYSYMBOL')
|
53
|
+
o.symbol.should == 'MYSYMBOL'
|
54
|
+
o.internal_symbol.should == 'AAPL_P250_14AUG2010'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'can show to number of business day until expiration' do
|
58
|
+
d = Date.today + 10
|
59
|
+
o = Option.new(Option::PUT, 'AAPL', 250, d.to_s)
|
60
|
+
o.days_to_exp.should < 10
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# require all files from lib
|
2
|
+
Dir["./lib/*.rb"].each {|file| require file }
|
3
|
+
|
4
|
+
describe YahooCrawler do
|
5
|
+
|
6
|
+
y = nil
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
y = YahooCrawler.new('AAPL', '2013-01-18')
|
10
|
+
y.fetch
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should be able to fetch options quotes from Yahoo!Finance' do
|
14
|
+
|
15
|
+
y.call_options.length.should > 0
|
16
|
+
y.put_options.length.should > 0
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should be able to get current price of stock' do
|
21
|
+
|
22
|
+
y.curr_stock_price.should > 0
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should be able to return an array of strike prices' do
|
27
|
+
|
28
|
+
y.call_strikes.length.should > 0
|
29
|
+
y.put_strikes.length.should > 0
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should be able to give an option quote by strike price' do
|
34
|
+
|
35
|
+
# get a good strike price by rounding the current stock price
|
36
|
+
good_price = ((y.curr_stock_price / 100).to_i * 100).to_f
|
37
|
+
|
38
|
+
call_quote = y.call_options[good_price]
|
39
|
+
call_quote.option.internal_symbol.should == "AAPL_C#{good_price.to_i}_18JAN2013"
|
40
|
+
call_quote.bid.should > 0 if not call_quote.bid.nil?
|
41
|
+
call_quote.ask.should > 0 if not call_quote.ask.nil?
|
42
|
+
|
43
|
+
put_quote = y.put_options[good_price]
|
44
|
+
put_quote.option.internal_symbol.should == "AAPL_P#{good_price.to_i}_18JAN2013"
|
45
|
+
put_quote.bid.should > 0 if not put_quote.bid.nil?
|
46
|
+
put_quote.ask.should > 0 if not put_quote.ask.nil?
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should be able to update the quotes in a background thread' do
|
51
|
+
|
52
|
+
y.auto_reload(1)
|
53
|
+
y.call_options.length.should > 0
|
54
|
+
y.put_options.length.should > 0
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: options-lib
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.9.2
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sergio Oliveira Jr.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-02-24 00:00:00 -08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mechanize
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
description: A set of classes for dealing with options. It includes a crawler for Yahoo!Finance.
|
39
|
+
email: sergio.oliveira.jr@gmail.com
|
40
|
+
executables:
|
41
|
+
- show_quotes
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- README.markdown
|
48
|
+
- bin/show_quotes
|
49
|
+
- lib/options-lib.rb
|
50
|
+
- lib/options-lib/helpers.rb
|
51
|
+
- lib/options-lib/option.rb
|
52
|
+
- lib/options-lib/option_quote.rb
|
53
|
+
- lib/options-lib/yahoo_crawler.rb
|
54
|
+
- options-lib.gemspec
|
55
|
+
- spec/helpers_spec.rb
|
56
|
+
- spec/option_quote_spec.rb
|
57
|
+
- spec/option_spec.rb
|
58
|
+
- spec/yahoo_crawler_spec.rb
|
59
|
+
has_rdoc: true
|
60
|
+
homepage: https://github.com/saoj/options-lib
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project: nowarning
|
83
|
+
rubygems_version: 1.5.2
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: Options Library
|
87
|
+
test_files:
|
88
|
+
- spec/helpers_spec.rb
|
89
|
+
- spec/option_quote_spec.rb
|
90
|
+
- spec/option_spec.rb
|
91
|
+
- spec/yahoo_crawler_spec.rb
|