ib-ruby 0.4.3 → 0.4.20

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.
Files changed (50) hide show
  1. data/.gitignore +32 -0
  2. data/HISTORY +68 -0
  3. data/README.rdoc +9 -6
  4. data/VERSION +1 -1
  5. data/bin/account_info +29 -0
  6. data/bin/contract_details +37 -0
  7. data/bin/depth_of_market +43 -0
  8. data/bin/historic_data +62 -0
  9. data/bin/{RequestHistoricData → historic_data_cli} +46 -91
  10. data/bin/market_data +49 -0
  11. data/bin/option_data +45 -0
  12. data/bin/template +21 -0
  13. data/bin/time_and_sales +63 -0
  14. data/lib/ib-ruby/connection.rb +166 -0
  15. data/lib/ib-ruby/constants.rb +91 -0
  16. data/lib/ib-ruby/messages/incoming.rb +807 -0
  17. data/lib/ib-ruby/messages/outgoing.rb +573 -0
  18. data/lib/ib-ruby/messages.rb +8 -1445
  19. data/lib/ib-ruby/models/bar.rb +26 -0
  20. data/lib/ib-ruby/models/contract.rb +335 -0
  21. data/lib/ib-ruby/models/execution.rb +55 -0
  22. data/lib/ib-ruby/models/model.rb +20 -0
  23. data/lib/ib-ruby/models/order.rb +262 -0
  24. data/lib/ib-ruby/models.rb +11 -0
  25. data/lib/ib-ruby/socket.rb +50 -0
  26. data/lib/ib-ruby/symbols/forex.rb +32 -72
  27. data/lib/ib-ruby/symbols/futures.rb +47 -68
  28. data/lib/ib-ruby/symbols/options.rb +30 -0
  29. data/lib/ib-ruby/symbols/stocks.rb +23 -0
  30. data/lib/ib-ruby/symbols.rb +9 -0
  31. data/lib/ib-ruby.rb +7 -8
  32. data/lib/legacy/bin/account_info_old +36 -0
  33. data/lib/legacy/bin/historic_data_old +81 -0
  34. data/lib/legacy/bin/market_data_old +68 -0
  35. data/lib/legacy/datatypes.rb +485 -0
  36. data/lib/legacy/ib-ruby.rb +10 -0
  37. data/lib/legacy/ib.rb +226 -0
  38. data/lib/legacy/messages.rb +1458 -0
  39. data/lib/version.rb +2 -3
  40. data/spec/ib-ruby/models/contract_spec.rb +261 -0
  41. data/spec/ib-ruby/models/order_spec.rb +64 -0
  42. data/spec/ib-ruby_spec.rb +0 -131
  43. metadata +106 -76
  44. data/bin/AccountInfo +0 -67
  45. data/bin/HistoricToCSV +0 -111
  46. data/bin/RequestMarketData +0 -78
  47. data/bin/SimpleTimeAndSales +0 -98
  48. data/bin/ib-ruby +0 -8
  49. data/lib/ib-ruby/datatypes.rb +0 -400
  50. data/lib/ib-ruby/ib.rb +0 -242
@@ -1,400 +0,0 @@
1
- #
2
- # Copyright (C) 2006 Blue Voodoo Magic LLC.
3
- #
4
- # This library is free software; you can redistribute it and/or modify
5
- # it under the terms of the GNU Lesser General Public License as
6
- # published by the Free Software Foundation; either version 2.1 of the
7
- # License, or (at your option) any later version.
8
- #
9
- # This library is distributed in the hope that it will be useful, but
10
- # WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
- # Lesser General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU Lesser General Public
15
- # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
- # 02110-1301 USA
18
- #
19
-
20
- #
21
- # TODO: Implement equals() according to the criteria in IB's Java client.
22
- #
23
-
24
- module IB
25
-
26
- module Datatypes
27
- attr_reader :created_at
28
-
29
- class AbstractDatum
30
- def init
31
- @created_at = Time.now
32
- end
33
-
34
- # If a hash is given, keys are taken as attribute names, values as data.
35
- # The attrs of the instance are set automatically from the attributeHash.
36
- #
37
- # If no hash is given, #init is called in the instance. #init
38
- # should set the datum up in a generic state.
39
- #
40
- def initialize(attributeHash=nil)
41
- if attributeHash.nil?
42
- init
43
- else
44
- raise(ArgumentError.new("Argument must be a Hash")) unless attributeHash.is_a?(Hash)
45
- attributeHash.keys.each {|key|
46
- self.send((key.to_s + "=").to_sym, attributeHash[key])
47
- }
48
- end
49
- end
50
- end # AbstractDatum
51
-
52
-
53
- # This is used within HistoricData messages.
54
- # Instantiate with a Hash of attributes, to be auto-set via initialize in AbstractDatum.
55
- class Bar < AbstractDatum
56
- attr_accessor :date, :open, :high, :low, :close, :volume, :wap, :has_gaps
57
-
58
- def to_s
59
- "<Bar: #{@date}; OHLC: #{@open.to_s}, #{@high.to_s}, #{@low.to_s}, #{@close.to_s}; volume: #{@volume}; wap: #{@wap.to_s}; has_gaps: #{@has_gaps}>"
60
- end
61
-
62
- end # Bar
63
-
64
-
65
- class Order < AbstractDatum
66
- # Constants used in Order objects. Drawn from Order.java
67
- Origin_Customer = 0
68
- Origin_Firm = 1
69
-
70
- Opt_Unknown = '?'
71
- Opt_Broker_Dealer = 'b'
72
- Opt_Customer = 'c'
73
- Opt_Firm = 'f'
74
- Opt_Isemm = 'm'
75
- Opt_Farmm = 'n'
76
- Opt_Specialist = 'y'
77
-
78
- # Main order fields
79
- attr_accessor(:id, :client_id, :perm_id, :action, :total_quantity, :order_type, :limit_price,
80
- :aux_price, :shares_allocation)
81
-
82
- # Extended order fields
83
- attr_accessor(:tif, :oca_group, :account, :open_close, :origin, :order_ref,
84
- :transmit, # if false, order will be created but not transmitted.
85
- :parent_id, # Parent order id, to associate auto STP or TRAIL orders with the original order.
86
- :block_order,
87
- :sweep_to_fill,
88
- :display_size,
89
- :trigger_method,
90
- :ignore_rth,
91
- :hidden,
92
- :discretionary_amount,
93
- :good_after_time,
94
- :good_till_date)
95
-
96
- OCA_Cancel_with_block = 1
97
- OCA_Reduce_with_block = 2
98
- OCA_Reduce_non_block = 3
99
-
100
- # No idea what the fa_* attributes are for, nor many of the others.
101
- attr_accessor(:fa_group, :fa_profile, :fa_method, :fa_profile, :fa_method, :fa_percentage, :primary_exchange,
102
- :short_sale_slot, # 1 or 2, says Order.java. (No idea what the difference is.)
103
- :designated_location, # "when slot=2 only"
104
- :oca_type, # 1 = CANCEL_WITH_BLOCK, 2 = REDUCE_WITH_BLOCK, 3 = REDUCE_NON_BLOCK
105
- :rth_only, :override_percentage_constraints, :rule_80a, :settling_firm, :all_or_none,
106
- :min_quantity, :percent_offset, :etrade_only, :firm_quote_only, :nbbo_price_cap)
107
-
108
- # Box orders only:
109
- Box_Auction_Match = 1
110
- Box_Auction_Improvement = 2
111
- Box_Auction_Transparent = 3
112
- attr_accessor(:auction_strategy, # Box_* constants above
113
- :starting_price, :stock_ref_price, :delta, :stock_range_lower, :stock_range_upper)
114
-
115
- # Volatility orders only:
116
- Volatility_Type_Daily = 1
117
- Volatility_Type_Annual = 2
118
-
119
- Volatility_Ref_Price_Average = 1
120
- Volatility_Ref_Price_BidOrAsk = 2
121
-
122
- attr_accessor(:volatility,
123
- :volatility_type, # 1 = daily, 2 = annual, as above
124
- :continuous_update,
125
- :reference_price_type, # 1 = average, 2 = BidOrAsk
126
- :delta_neutral_order_type,
127
- :delta_neutral_aux_price)
128
-
129
- Max_value = 99999999 # I don't know why IB uses a very large number as the default for certain fields
130
- def init
131
- super
132
-
133
- @open_close = "0"
134
- @origin = Origin_Customer
135
- @transmit = true
136
- @primary_exchange = ''
137
- @designated_location = ''
138
- @min_quantity = Max_value
139
- @percent_offset = Max_value
140
- @nbba_price_cap = Max_value
141
- @starting_price = Max_value
142
- @stock_ref_price = Max_value
143
- @delta = Max_value
144
- @delta_neutral_order_type = ''
145
- @delta_neutral_aux_price = Max_value
146
- @reference_price_type = Max_value
147
- end # init
148
-
149
- end # class Order
150
-
151
-
152
- class Contract < AbstractDatum
153
-
154
- # Valid security types (sec_type attribute)
155
- SECURITY_TYPES =
156
- {
157
- :stock => "STK",
158
- :option => "OPT",
159
- :future => "FUT",
160
- :index => "IND",
161
- :futures_option => "FOP",
162
- :forex => "CASH",
163
- :bag => "BAG"
164
- }
165
-
166
- # note that the :description field is entirely local to ib-ruby, and not part of TWS.
167
- # You can use it to store whatever arbitrary data you want.
168
-
169
- attr_accessor(:symbol, :strike, :multiplier, :exchange, :currency,
170
- :local_symbol, :combo_legs, :description)
171
-
172
- # Bond values
173
- attr_accessor(:cusip, :ratings, :desc_append, :bond_type, :coupon_type, :callable, :puttable,
174
- :coupon, :convertible, :maturity, :issue_date)
175
-
176
- attr_reader :sec_type, :expiry, :right, :primary_exchange
177
-
178
-
179
-
180
- # some protective filters
181
-
182
- def primary_exchange=(x)
183
- x.upcase! if x.is_a?(String)
184
-
185
- # per http://chuckcaplan.com/twsapi/index.php/Class%20Contract
186
- raise(ArgumentError.new("Don't set primary_exchange to smart")) if x == "SMART"
187
-
188
- @primary_exchange = x
189
- end
190
-
191
- def right=(x)
192
- x.upcase! if x.is_a?(String)
193
- x = nil if !x.nil? && x.empty?
194
- raise(ArgumentError.new("Invalid right \"#{x}\" (must be one of PUT, CALL, P, C)")) unless x.nil? || [ "PUT", "CALL", "P", "C", "0"].include?(x)
195
- @right = x
196
- end
197
-
198
- def expiry=(x)
199
- x = x.to_s
200
- if (x.nil? || ! (x =~ /\d{6,8}/)) and !x.empty? then
201
- raise ArgumentError.new("Invalid expiry \"#{x}\" (must be in format YYYYMM or YYYYMMDD)")
202
- end
203
- @expiry = x
204
- end
205
-
206
- def sec_type=(x)
207
- x = nil if !x.nil? && x.empty?
208
- raise(ArgumentError.new("Invalid security type \"#{x}\" (see SECURITY_TYPES constant in Contract class for valid types)")) unless x.nil? || SECURITY_TYPES.values.include?(x)
209
- @sec_type = x
210
- end
211
-
212
- def reset
213
- @combo_legs = Array.new
214
- @strike = 0
215
- end
216
-
217
- # Different messages serialize contracts differently. Go figure.
218
- def serialize_short(version)
219
- q = [ self.symbol,
220
- self.sec_type,
221
- self.expiry,
222
- self.strike,
223
- self.right ]
224
-
225
- q.push(self.multiplier) if version >= 15
226
- q.concat([
227
- self.exchange,
228
- self.currency,
229
- self.local_symbol
230
- ])
231
-
232
- q
233
- end # serialize
234
-
235
- # This returns an Array of data from the given contract, in standard format.
236
- # Note that it does not include the combo legs.
237
- def serialize_long(version)
238
- queue = [
239
- self.symbol,
240
- self.sec_type,
241
- self.expiry,
242
- self.strike,
243
- self.right
244
- ]
245
-
246
- queue.push(self.multiplier) if version >= 15
247
- queue.push(self.exchange)
248
- queue.push(self.primary_exchange) if version >= 14
249
- queue.push(self.currency)
250
- queue.push(self.local_symbol) if version >= 2
251
-
252
- queue
253
- end # serialize_long
254
-
255
- #
256
- # This produces a string uniquely identifying this contract, in the format used
257
- # for command line arguments in the IB-Ruby examples. The format is:
258
- #
259
- # symbol:security_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
260
- #
261
- # Fields not needed for a particular security should be left blank (e.g. strike and right are only relevant for options.)
262
- #
263
- # For example, to query the British pound futures contract trading on Globex expiring in September, 2008,
264
- # the string is:
265
- #
266
- # GBP:FUT:200809:::62500:GLOBEX::USD:
267
- #
268
-
269
- def serialize_ib_ruby(version)
270
- serialize_long(version).join(":")
271
- end
272
-
273
- # This returns a Contract initialized from the serialize_ib_ruby format string.
274
- def self.from_ib_ruby(string)
275
- c = Contract.new
276
- c.symbol, c.sec_type, c.expiry, c.strike, c.right, c.multiplier, c.exchange, c.primary_exchange, c.currency, c.local_symbol = string.split(":")
277
-
278
- c
279
- end
280
-
281
- # Some messages send open_close too, some don't. WTF.
282
- def serialize_combo_legs(include_open_close = false)
283
- if self.combo_legs.nil?
284
- [0]
285
- else
286
- [ self.combo_legs.size ].concat(self.combo_legs.serialize(include_open_close))
287
- end
288
- end
289
-
290
- def init
291
- super
292
-
293
- @combo_legs = Array.new
294
- @strike = 0
295
- @sec_type = ''
296
- end
297
-
298
- def to_human
299
- "<IB-Contract: " + [symbol, expiry, sec_type, strike, right, exchange, currency].join("-") + "}>"
300
- end
301
-
302
- def to_short
303
- "#{symbol}#{expiry}#{strike}#{right}#{exchange}#{currency}"
304
- end
305
-
306
- def to_s
307
- to_human
308
- end
309
-
310
- end # class Contract
311
-
312
-
313
- class ContractDetails < AbstractDatum
314
- attr_accessor :summary, :market_name, :trading_class, :con_id, :min_tick, :multiplier, :price_magnifier, :order_types, :valid_exchanges
315
-
316
- def init
317
- super
318
-
319
- @summary = Contract.new
320
- @con_id = 0
321
- @min_tick = 0
322
- end
323
- end # class ContractDetails
324
-
325
-
326
- class Execution < AbstractDatum
327
- attr_accessor :order_id, :client_id, :exec_id, :time, :account_number, :exchange, :side, :shares, :price, :perm_id, :liquidation
328
-
329
- def init
330
- super
331
-
332
- @order_id = 0
333
- @client_id = 0
334
- @shares = 0
335
- @price = 0
336
- @perm_id = 0
337
- @liquidation =0
338
- end
339
- end # Execution
340
-
341
- # EClientSocket.java tells us: 'Note that the valid format for m_time is "yyyymmdd-hh:mm:ss"'
342
- class ExecutionFilter < AbstractDatum
343
- attr_accessor :client_id, :acct_code, :time, :symbol, :sec_type, :exchange, :side
344
-
345
- def init
346
- super
347
-
348
- @client_id = 0
349
- end
350
-
351
- end # ExecutionFilter
352
-
353
-
354
- class ComboLeg < AbstractDatum
355
- attr_accessor :con_id, :ratio, :action, :exchange, :open_close
356
-
357
- def init
358
- super
359
-
360
- @con_id = 0
361
- @ratio = 0
362
- @open_close = 0
363
- end
364
-
365
- # Some messages include open_close, some don't. wtf.
366
- def serialize(include_open_close = false)
367
- self.collect { |leg|
368
- [ leg.con_id, leg.ratio, leg.action, leg.exchange, (include_open_close ? leg.open_close : [] )]
369
- }.flatten
370
- end
371
- end # ComboLeg
372
-
373
-
374
- class ScannerSubscription < AbstractDatum
375
- attr_accessor :number_of_rows, :instrument, :location_code, :scan_code, :above_price, :below_price,
376
- :above_volume, :average_option_volume_above, :market_cap_above, :market_cap_below, :moody_rating_above,
377
- :moody_rating_below, :sp_rating_above, :sp_rating_below, :maturity_date_above, :maturity_date_below,
378
- :coupon_rate_above, :coupon_rate_below, :exclude_convertible, :scanner_setting_pairs, :stock_type_filter
379
-
380
- def init
381
- super
382
-
383
- @coupon_rate_above = @coupon_rate_below = @market_cap_below = @market_cap_above = @average_option_volume_above =
384
- @above_volume = @below_price = @above_price = nil
385
- @number_of_rows = -1 # none specified, per ScannerSubscription.java
386
- end
387
- end # ScannerSubscription
388
-
389
-
390
- # Just like a Hash, but throws an exception if you try to access a key that doesn't exist.
391
- class StringentHash < Hash
392
- def initialize(hash)
393
- super() {|hash,key| raise Exception.new("key #{key.inspect} not found!") }
394
- self.merge!(hash) unless hash.nil?
395
- end
396
- end
397
-
398
- end # module Datatypes
399
-
400
- end # module
data/lib/ib-ruby/ib.rb DELETED
@@ -1,242 +0,0 @@
1
- #
2
- # Copyright (C) 2006 Blue Voodoo Magic LLC.
3
- #
4
- # This library is free software; you can redistribute it and/or modify
5
- # it under the terms of the GNU Lesser General Public License as
6
- # published by the Free Software Foundation; either version 2.1 of the
7
- # License, or (at your option) any later version.
8
- #
9
- # This library is distributed in the hope that it will be useful, but
10
- # WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
- # Lesser General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU Lesser General Public
15
- # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
- # 02110-1301 USA
18
- #
19
-
20
- require 'sha1'
21
- require 'socket'
22
- require 'logger'
23
- require 'bigdecimal'
24
- require 'bigdecimal/util'
25
-
26
- # Add method to_ib to render datetime in IB format (zero padded "yyyymmdd HH:mm:ss")
27
- class Time
28
- def to_ib
29
- "#{self.year}#{sprintf("%02d", self.month)}#{sprintf("%02d", self.day)} " +
30
- "#{sprintf("%02d", self.hour)}:#{sprintf("%02d", self.min)}:#{sprintf("%02d", self.sec)}"
31
- end
32
- end # Time
33
-
34
-
35
- module IB
36
-
37
- TWS_IP_ADDRESS = "127.0.0.1"
38
- TWS_PORT = "7496"
39
-
40
- #logger = Logger.new(STDERR)
41
-
42
- class IBSocket < TCPSocket
43
-
44
- # send nice null terminated binary data
45
- def send(data)
46
- self.syswrite(data.to_s + "\0")
47
- end
48
-
49
- def read_string
50
- self.gets("\0").chop
51
- end
52
-
53
- def read_int
54
- self.read_string.to_i
55
- end
56
-
57
- def read_boolean
58
- self.read_string.to_i != 0
59
- end
60
-
61
- # Floating-point numbers shouldn't be used to store money.
62
- def read_decimal
63
- self.read_string.to_d
64
- end
65
-
66
- end # class IBSocket
67
-
68
-
69
-
70
- class IB
71
- Tws_client_version = 27
72
-
73
- attr_reader :next_order_id
74
-
75
- def initialize(options_in = {})
76
- @options = {
77
- :ip => TWS_IP_ADDRESS,
78
- :port => TWS_PORT,
79
- }.merge(options_in)
80
-
81
- @connected = false
82
- @next_order_id = nil
83
- @server = Hash.new # information about server and server connection state
84
-
85
- # Message listeners.
86
- # Key is the message class to listen for.
87
- # Value is an Array of Procs. The proc will be called with the populated message instance as its argument when
88
- # a message of that type is received.
89
- @listeners = Hash.new { |hash, key|
90
- hash[key] = Array.new
91
- }
92
-
93
-
94
- #logger.debug("IB#init: Initializing...")
95
-
96
- self.open(@options)
97
-
98
- end # init
99
-
100
- def server_version
101
- @server[:version]
102
- end
103
-
104
-
105
- def open(options_in = {})
106
- raise Exception.new("Already connected!") if @connected
107
-
108
- opts = {
109
- :ip => "127.0.0.1",
110
- :port => "7496"
111
- }.merge(options_in)
112
-
113
-
114
- # Subscribe to the NextValidID message from TWS that is always
115
- # sent at connect, and save the id.
116
- self.subscribe(IncomingMessages::NextValidID, lambda {|msg|
117
- @next_order_id = msg.data[:order_id]
118
- #logger.info { "Got next valid order id #{@next_order_id}." }
119
- })
120
-
121
- @server[:socket] = IBSocket.open(@options[:ip], @options[:port])
122
- #logger.info("* TWS socket connected to #{@options[:ip]}:#{@options[:port]}.")
123
-
124
- # Sekrit handshake.
125
- #logger.debug("\tSending client version #{Tws_client_version}..")
126
-
127
- @server[:socket].send(Tws_client_version)
128
- @server[:version] = @server[:socket].read_int
129
- @@server_version = @server[:version]
130
- @server[:local_connect_time] = Time.now()
131
-
132
- #logger.debug("\tGot server version: #{@server[:version]}.")
133
-
134
- # Server version >= 20 sends the server time back.
135
- if @server[:version] >= 20
136
- @server[:remote_connect_time] = @server[:socket].read_string
137
- #logger.debug("\tServer connect time: #{@server[:remote_connect_time]}.")
138
- end
139
-
140
- # Server version >= 3 wants an arbitrary client ID at this point. This can be used
141
- # to identify subsequent communications.
142
- if @server[:version] >= 3
143
- @server[:client_id] = SHA1.digest(Time.now.to_s + $$.to_s).unpack("C*").join.to_i % 999999999
144
- @server[:socket].send(@server[:client_id])
145
- #logger.debug("\tSent client id # #{@server[:client_id]}.")
146
- end
147
-
148
- #logger.debug("Starting reader thread..")
149
- Thread.abort_on_exception = true
150
- @server[:reader_thread] = Thread.new {
151
- self.reader
152
- }
153
-
154
- @connected = true
155
- end
156
-
157
-
158
-
159
- def close
160
- @server[:reader_thread].kill # Thread uses blocking I/O, so join is useless.
161
- @server[:socket].close()
162
- @server = Hash.new
163
- @@server_version = nil
164
- @connected = false
165
- #logger.debug("Disconnected.")
166
- end # close
167
-
168
-
169
-
170
- def to_s
171
- "IB Connector: #{ @connected ? "connected." : "disconnected."}"
172
- end
173
-
174
-
175
-
176
- # Subscribe to incoming message events of type messageClass.
177
- # code is a Proc that will be called with the message instance as its argument.
178
- def subscribe(messageClass, code)
179
- raise(Exception.new("Invalid argument type (#{messageClass}, #{code.class}) - " +
180
- " must be (IncomingMessages::AbstractMessage, Proc)")) unless
181
- messageClass <= IncomingMessages::AbstractMessage && code.is_a?(Proc)
182
-
183
- @listeners[messageClass].push(code)
184
- end
185
-
186
-
187
-
188
- # Send an outgoing message.
189
- def dispatch(message)
190
- raise Exception.new("dispatch() must be given an OutgoingMessages::AbstractMessage subclass") unless
191
- message.is_a?(OutgoingMessages::AbstractMessage)
192
-
193
- #logger.info("Sending message " + message.inspect)
194
- message.send(@server)
195
- end
196
-
197
-
198
-
199
- protected
200
-
201
- def reader
202
- #logger.debug("Reader started.")
203
-
204
- while true
205
- msg_id = @server[:socket].read_int # this blocks, so Thread#join is useless.
206
- #logger.debug { "Reader: got message id #{msg_id}.\n" }
207
-
208
- # create a new instance of the appropriate message type, and have it read the message.
209
- msg = IncomingMessages::Table[msg_id].new(@server[:socket], @server[:version])
210
-
211
- @listeners[msg.class].each { |listener|
212
- listener.call(msg)
213
- }
214
-
215
- #logger.debug { " Listeners: " + @listeners.inspect + " inclusion: #{ @listeners.include?(msg.class)}" }
216
-
217
- # Log the message if it's an error.
218
- # Make an exception for the "successfully connected" messages, which, for some reason, come back from IB as errors.
219
- if msg.is_a?(IncomingMessages::Error)
220
- if msg.code == 2104 || msg.code == 2106 # connect strings
221
- #logger.info(msg.to_human)
222
- else
223
- #logger.error(msg.to_human)
224
- end
225
- else
226
- # Warn if nobody listened to a non-error incoming message.
227
- unless @listeners[msg.class].size > 0
228
- #logger.warn { " WARNING: Nobody listened to incoming message #{msg.class}" }
229
- end
230
- end
231
-
232
-
233
- # #logger.debug("Reader done with message id #{msg_id}.")
234
-
235
-
236
- end # while
237
-
238
- #logger.debug("Reader done.")
239
- end # reader
240
-
241
- end # class IB
242
- end # module IB