Class: Oh

Inherits:
Object
  • Object
show all
Defined in:
lib/oh.rb

Constant Summary collapse

VERSION =
'1.0.3'
HOST =
"www.optionshouse.com"
HEADERS =
{
  "Host" => HOST,
  "User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12 (.NET CLR 3.5.30729)",
  "Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  "Accept-Language" => "en-us,en;q=0.5",
  "Accept-Encoding" => "gzip,deflate",
  "Accept-Charset" => "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
  "Connection" => "keep-alive",
  "Content-Type" => "text/xml; charset=UTF-8",
  "Referer" => "https://www.optionshouse.com/securehost/tool/login/",
  "Cookie" => "blackbird={pos:1,size:0,load:null,info:true,debug:true,warn:true,error:true,profile:true}",
  "Pragma" => "no-cache",
  "Cache-Control" => "no-cache",
}
AuthError =
Class.new(StandardError)
AccountError =
Class.new(StandardError)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(username, password) ⇒ Oh

Returns a new instance of Oh.



33
34
35
36
# File 'lib/oh.rb', line 33

def initialize(username, password)
  self.username = username
  self.password = password
end

Instance Attribute Details

#account_idObject



38
39
40
# File 'lib/oh.rb', line 38

def 
  @account_id or raise "Account not set; call Oh#accounts() to get a list, then set using Oh#account_id=(id)."
end

#passwordObject

Returns the value of attribute password.



30
31
32
# File 'lib/oh.rb', line 30

def password
  @password
end

#usernameObject

Returns the value of attribute username.



30
31
32
# File 'lib/oh.rb', line 30

def username
  @username
end

Instance Method Details

#accountsObject



66
67
68
# File 'lib/oh.rb', line 66

def accounts
  request(message_with_token("account.info"))
end

#chain_message(symbol) ⇒ Object



97
98
99
100
101
102
103
104
105
106
# File 'lib/oh.rb', line 97

def chain_message(symbol)
  ("view.chain",
                       :symbol => symbol,
                       :greeks => true,
                       :weeklies => true,
                       :quarterlies => true,
                       :quotesAfter => 0,
                       :ntm => 10, # near the money
                       :bs => true) # black-scholes?
end

#check_connection_status(doc) ⇒ Object



181
182
183
184
185
# File 'lib/oh.rb', line 181

def check_connection_status(doc)
  if doc.at("//errors/access[text()='denied']")
    raise AuthError, "Access denied, token has expired?"
  end
end

#connectionObject



187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/oh.rb', line 187

def connection
  return @client if defined? @client

  client = Net::HTTP.new(HOST, 443)
  client.use_ssl = true

  if ENV["SSL_PATH"] && File.exist?(ENV["SSL_PATH"])
    client.ca_path = ENV["SSL_PATH"]
    client.verify_mode = OpenSSL::SSL::VERIFY_PEER
  end

  @client = client
end

#keep_aliveObject

TODO: send this every 120 seconds?



83
84
85
# File 'lib/oh.rb', line 83

def keep_alive
  request(("auth.keepAlive"))
end

#loginObject

Raises:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/oh.rb', line 46

def 
  response = request_without_callbacks(message("auth.login",
                  :userName => username,
                  :password => password,
                  :organization => "OPHOUSE,KERSHNER",
                  :authToken => nil,
                  :validationText => ""))

  access = response.search("//access").text
  raise AuthError, "Access was #{access}" unless access == "granted"

  ready = response.search("//requiresAccountCreation").text == "false"
  raise AccountError, "Account is not active" unless ready

  token = response.search("//authToken").text
  raise AuthError, "No auth token was returned" if token.strip.empty? or token == "null"

  token
end

#message(action, data = {}) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/oh.rb', line 116

def message(action, data = {})
  chunks = []

  chunks << "<EZMessage action='#{action}'>"
  chunks << "<data>"

  data.each do |key, value|
    chunks << "<#{key}>#{value || "null"}</#{key}>"
  end

  chunks << "</data>"
  chunks << "</EZMessage>"

  chunks.join
end

#message_with_account(action, data = {}) ⇒ Object



112
113
114
# File 'lib/oh.rb', line 112

def (action, data = {})
  message_with_token(action, {:account => }.merge(data))
end

#message_with_token(action, data = {}) ⇒ Object



108
109
110
# File 'lib/oh.rb', line 108

def message_with_token(action, data = {})
  message(action, {:authToken => token}.merge(data))
end

#messages(*messages) ⇒ Object



132
133
134
# File 'lib/oh.rb', line 132

def messages(*messages)
  "<EZList>#{messages.flatten.join}</EZList>"
end

#option_chain(symbol) ⇒ Object



74
75
76
# File 'lib/oh.rb', line 74

def option_chain(symbol)
  request(chain_message(symbol))
end

#quote(symbol) ⇒ Object



70
71
72
# File 'lib/oh.rb', line 70

def quote(symbol)
  request(messages(quote_messages(symbol)))
end

#quote_messages(symbol) ⇒ Object



87
88
89
90
91
92
93
94
95
# File 'lib/oh.rb', line 87

def quote_messages(symbol)
  [
    ("view.quote",
                         :symbol => symbol,
                         :description => true,
                         :fundamentals => true),
    message_with_token("echo", :symbol => symbol)
  ]
end

#quote_with_chain(symbol) ⇒ Object



78
79
80
# File 'lib/oh.rb', line 78

def quote_with_chain(symbol)
  request(messages(quote_messages(symbol), chain_message(symbol)))
end

#request(body, with_callbacks = true) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/oh.rb', line 140

def request(body, with_callbacks = true)
  path = "/m"

  response = connection.post(path, body, HEADERS)
  data = response.body

  out = case response["content-encoding"]
    when /gzip/    then Zlib::GzipReader.new(StringIO.new(data)).read
    when /deflate/ then Zlib::Inflate.inflate(data)
    else data
  end

  if $DEBUG
    puts "Sent:"
    puts body
    puts "-" * 80
    puts "Got"
    p response.code
    p response.message

    response.each {|key, val| puts key + ' = ' + val}

    puts out
    puts "=" * 80
    puts
  end

  result = begin
    Nokogiri.parse(out, nil, nil, Nokogiri::XML::ParseOptions::STRICT)
  rescue
    warn "Unable to parse: #{out.inspect}"
    raise
  end

  check_connection_status(result)

  with_callbacks && respond_to?(:post_process_request) ? 
    post_process_request(result) : 
    result
end

#request_without_callbacks(body) ⇒ Object



136
137
138
# File 'lib/oh.rb', line 136

def request_without_callbacks(body)
  request(body, false)
end

#tokenObject



42
43
44
# File 'lib/oh.rb', line 42

def token
  @token ||= 
end