ib-ruby 0.5.19 → 0.5.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/HISTORY +4 -0
  2. data/TODO +0 -3
  3. data/VERSION +1 -1
  4. data/bin/contract_details +3 -3
  5. data/bin/depth_of_market +1 -1
  6. data/bin/historic_data +5 -8
  7. data/bin/market_data +2 -2
  8. data/bin/option_data +2 -2
  9. data/lib/ib-ruby/connection.rb +1 -9
  10. data/lib/ib-ruby/extensions.rb +8 -0
  11. data/lib/ib-ruby/messages/abstract_message.rb +89 -0
  12. data/lib/ib-ruby/messages/incoming.rb +415 -487
  13. data/lib/ib-ruby/messages/outgoing.rb +241 -305
  14. data/lib/ib-ruby/models/bar.rb +3 -3
  15. data/lib/ib-ruby/models/contract/bag.rb +1 -5
  16. data/lib/ib-ruby/models/contract.rb +50 -33
  17. data/lib/ib-ruby/models/execution.rb +6 -3
  18. data/lib/ib-ruby/models/order.rb +7 -5
  19. data/lib/ib-ruby/socket.rb +13 -0
  20. data/lib/ib-ruby/symbols/forex.rb +7 -14
  21. data/lib/ib-ruby/symbols/futures.rb +16 -20
  22. data/lib/ib-ruby/symbols/options.rb +6 -4
  23. data/lib/ib-ruby/symbols/stocks.rb +1 -1
  24. data/lib/ib-ruby.rb +1 -0
  25. data/spec/README.md +34 -0
  26. data/spec/ib-ruby/connection_spec.rb +4 -4
  27. data/spec/ib-ruby/messages/incoming_spec.rb +50 -0
  28. data/spec/ib-ruby/messages/outgoing_spec.rb +32 -0
  29. data/spec/ib-ruby/models/contract_spec.rb +27 -25
  30. data/spec/ib-ruby/models/order_spec.rb +56 -23
  31. data/spec/integration/account_info_spec.rb +85 -0
  32. data/spec/integration/contract_info_spec.rb +209 -0
  33. data/spec/integration/depth_data_spec.rb +46 -0
  34. data/spec/integration/historic_data_spec.rb +82 -0
  35. data/spec/integration/market_data_spec.rb +97 -0
  36. data/spec/integration/option_data_spec.rb +96 -0
  37. data/spec/integration/orders/execution_spec.rb +135 -0
  38. data/spec/{ib-ruby/messages → integration/orders}/open_order +9 -205
  39. data/spec/integration/orders/placement_spec.rb +150 -0
  40. data/spec/integration/orders/valid_ids_spec.rb +84 -0
  41. data/spec/integration_helper.rb +110 -0
  42. data/spec/message_helper.rb +13 -18
  43. data/spec/spec_helper.rb +35 -26
  44. metadata +33 -17
  45. data/spec/ib-ruby/messages/README.md +0 -16
  46. data/spec/ib-ruby/messages/account_info_spec.rb +0 -84
  47. data/spec/ib-ruby/messages/just_connect_spec.rb +0 -33
  48. data/spec/ib-ruby/messages/market_data_spec.rb +0 -92
  49. data/spec/ib-ruby/messages/orders_spec.rb +0 -219
  50. data/spec/ib-ruby_spec.rb +0 -0
@@ -1,3 +1,5 @@
1
+ require 'ib-ruby/messages/abstract_message'
2
+
1
3
  # EClientSocket.java uses sendMax() rather than send() for a number of these.
2
4
  # It sends an EOL rather than a number if the value == Integer.MAX_VALUE (or Double.MAX_VALUE).
3
5
  # These fields are initialized to this MAX_VALUE.
@@ -11,82 +13,64 @@ module IB
11
13
 
12
14
  # Incoming IB messages
13
15
  module Incoming
16
+ extend Messages # def_message macros
17
+
14
18
  Classes = Array.new
15
19
 
16
- # This is just a basic generic message from the server.
17
- #
18
- # Class variables:
19
- # @message_id - int: message id.
20
- # @version - int: current version of message format.
21
- #
22
- # Instance attributes (at least):
23
- # @data - Hash of actual data read from a stream.
24
- #
25
- # Override the load(socket) method in your subclass to do actual reading into @data.
26
- class AbstractMessage
27
- attr_accessor :created_at, :data
20
+ class AbstractMessage < IB::Messages::AbstractMessage
28
21
 
29
22
  def self.inherited(by)
30
23
  super(by)
31
24
  Classes.push(by)
32
25
  end
33
26
 
34
- def self.message_id
35
- @message_id
27
+ def version # Per message, received messages may have the different versions
28
+ @data[:version]
36
29
  end
37
30
 
38
- def initialize socket
39
- raise Exception.new("Don't use AbstractMessage directly; use the subclass for your specific message type") if self.class.name == "AbstractMessage"
31
+ # Read incoming message from given socket or instantiate with given data
32
+ def initialize socket_or_data
40
33
  @created_at = Time.now
41
- @data = Hash.new
42
- @socket = socket
43
-
44
- self.load()
45
-
46
- @socket = nil
47
- end
48
-
49
- def to_human
50
- self.inspect
51
- end
52
-
53
- # Object#id is always defined, we cannot rely on method_missing
54
- def id
55
- @data.has_key?(:id) ? @data[:id] : super
56
- end
57
-
58
- def respond_to? method
59
- getter = method.to_s.sub(/=$/, '').to_sym
60
- @data.has_key?(method) || @data.has_key?(getter) || super
61
- end
62
-
63
- protected
64
-
65
- # TODO: method compilation instead of method_missing
66
- def method_missing method, *args
67
- getter = method.to_s.sub(/=$/, '').to_sym
68
- if @data.has_key? method
69
- @data[method]
70
- elsif @data.has_key? getter
71
- @data[getter] = *args
34
+ if socket_or_data.is_a?(Hash)
35
+ @data = socket_or_data
72
36
  else
73
- super method, *args
37
+ @data = {}
38
+ @socket = socket_or_data
39
+ self.load
40
+ @socket = nil
74
41
  end
75
42
  end
76
43
 
77
44
  # Every message loads received message version first
78
45
  def load
79
46
  @data[:version] = @socket.read_int
47
+
48
+ if @data[:version] != self.class.version
49
+ raise "Unsupported version #{@data[:version]} of #{self.class} received"
50
+ end
51
+
52
+ load_map *self.class.data_map
80
53
  end
81
54
 
82
55
  # Load @data from the socket according to the given map.
83
56
  #
84
- # map is a series of Arrays in the format [ [ :name, :type ] ],
57
+ # map is a series of Arrays in the format of
58
+ # [ [ :name, :type ],
59
+ # [ :group, :name, :type] ]
85
60
  # type identifiers must have a corresponding read_type method on socket (read_int, etc.).
86
- # [:version, :int ] is loaded first, by default
87
- #
61
+ # group is used to lump together aggregates, such as Contract or Order fields
88
62
  def load_map(*map)
89
- map.each { |(name, type)| @data[name] = @socket.__send__("read_#{type}") }
63
+ map.each do |(m1, m2, m3)|
64
+ group, name, type = m3 ? [m1, m2, m3] : [nil, m1, m2]
65
+
66
+ data = @socket.__send__("read_#{type}")
67
+ if group
68
+ @data[group] ||= {}
69
+ @data[group][name] = data
70
+ else
71
+ @data[name] = data
72
+ end
73
+ end
90
74
  end
91
75
  end # class AbstractMessage
92
76
 
@@ -97,28 +81,13 @@ module IB
97
81
  end
98
82
 
99
83
  def to_human
100
- "<#{self.class.to_s.split('::').last} #{type}:" +
84
+ "<#{self.message_type} #{type}:" +
101
85
  @data.map do |key, value|
102
- " #{key} #{value}" unless [:version, :id, :tick_type].include?(key)
86
+ " #{key} #{value}" unless [:version, :ticker_id, :tick_type].include?(key)
103
87
  end.compact.join(',') + " >"
104
88
  end
105
89
  end
106
90
 
107
- # Macro that defines short message classes using a one-liner
108
- def self.def_message message_id, *keys, &to_human
109
- base = keys.first.is_a?(Class) ? keys.shift : AbstractMessage
110
- Class.new(base) do
111
- @message_id = message_id
112
-
113
- define_method(:load) do
114
- super()
115
- load_map *keys
116
- end
117
-
118
- define_method(:to_human, &to_human) if to_human
119
- end
120
- end
121
-
122
91
  ### Actual message classes (short definitions):
123
92
  #:status - String: Displays the order status. Possible values include:
124
93
  # � PendingSubmit - indicates that you have transmitted the order, but
@@ -147,7 +116,7 @@ module IB
147
116
  # the order is inactive due to system, exchange or other issues.
148
117
  # :why_held - This field is used to identify an order held when TWS is trying to
149
118
  # locate shares for a short sell. The value used to indicate this is 'locate'.
150
- OrderStatus = def_message 3, [:id, :int],
119
+ OrderStatus = def_message [3, 6], [:order_id, :int],
151
120
  [:status, :string],
152
121
  [:filled, :int],
153
122
  [:remaining, :int],
@@ -161,28 +130,26 @@ module IB
161
130
  " @ last/avg: #{last_fill_price}/#{average_fill_price}" +
162
131
  (parent_id > 0 ? "parent_id: #{parent_id}" : "") +
163
132
  (why_held != "" ? "why_held: #{why_held}" : "") +
164
- " id/perm: #{id}/#{perm_id}>"
133
+ " id/perm: #{order_id}/#{perm_id}>"
165
134
  end
166
135
 
167
136
 
168
- AccountValue = def_message(6, [:key, :string],
137
+ AccountValue = def_message([6, 2], [:key, :string],
169
138
  [:value, :string],
170
139
  [:currency, :string],
171
140
  [:account_name, :string]) do
172
141
  "<AccountValue: #{account_name}, #{key}=#{value} #{currency}>"
173
142
  end
174
143
 
175
- AccountUpdateTime = def_message(8, [:time_stamp, :string]) do
176
- "<AccountUpdateTime: #{time_stamp}>"
177
- end
144
+ AccountUpdateTime = def_message 8, [:time_stamp, :string]
178
145
 
179
146
  # This message is always sent by TWS automatically at connect.
180
147
  # The IB::Connection class subscribes to it automatically and stores
181
148
  # the order id in its @next_order_id attribute.
182
- NextValidID = def_message(9, [:id, :int]) { "<NextValidID: #{id}>" }
149
+ NextValidID = def_message 9, [:order_id, :int]
183
150
 
184
151
  NewsBulletins =
185
- def_message 14, [:id, :int], # unique incrementing bulletin ID.
152
+ def_message 14, [:request_id, :int], # unique incrementing bulletin ID.
186
153
  [:type, :int], # Type of bulletin. Valid values include:
187
154
  # 1 = Regular news bulletin
188
155
  # 2 = Exchange no longer available for trading
@@ -200,8 +167,7 @@ module IB
200
167
  # 1 = GROUPS
201
168
  # 2 = PROFILE
202
169
  # 3 = ACCOUNT ALIASES
203
- [:xml, :string] # XML string containing the previously requested
204
- # FA configuration information.
170
+ [:xml, :string] # XML string with requested FA configuration information.
205
171
 
206
172
  # Receives an XML document that describes the valid parameters that a scanner
207
173
  # subscription can have (for outgoing RequestScannerSubscription message).
@@ -212,21 +178,18 @@ module IB
212
178
 
213
179
  # Receive Reuters global fundamental market data. There must be a subscription to
214
180
  # Reuters Fundamental set up in Account Management before you can receive this data.
215
- FundamentalData = def_message 50, [:id, :int], # request_id
181
+ FundamentalData = def_message 50, [:request_id, :int], # request_id
216
182
  [:data, :string]
217
183
 
218
- ContractDataEnd = def_message(52, [:id, :int]) { "<ContractDataEnd: #{id}>" } # request_id
219
-
220
- OpenOrderEnd = def_message(53) { "<OpenOrderEnd>" }
184
+ ContractDataEnd = def_message 52, [:request_id, :int] # request_id
221
185
 
222
- AccountDownloadEnd = def_message(54, [:account_name, :string]) do
223
- "<AccountDownloadEnd: #{account_name}}>"
224
- end # request_id
186
+ OpenOrderEnd = def_message 53
225
187
 
188
+ AccountDownloadEnd = def_message 54, [:account_name, :string]
226
189
 
227
- ExecutionDataEnd = def_message(55, [:id, :int]) { "<ExecutionDataEnd: #{id}>" } # request_id
190
+ ExecutionDataEnd = def_message 55, [:request_id, :int] # request_id
228
191
 
229
- TickSnapshotEnd = def_message(57, [:id, :int]) { "<TickSnapshotEnd: #{id}>" } # request_id
192
+ TickSnapshotEnd = def_message 57, [:ticker_id, :int]
230
193
 
231
194
  ### Actual message classes (long definitions):
232
195
 
@@ -271,30 +234,30 @@ module IB
271
234
  # IB then emits at most 2 events on eWrapper:
272
235
  # tickPrice( tickerId, tickType, price, canAutoExecute)
273
236
  # tickSize( tickerId, sizeTickType, size)
274
- TickPrice = def_message 1, AbstractTick,
275
- [:id, :int], # ticker_id
237
+ TickPrice = def_message [1, 6], AbstractTick,
238
+ [:ticker_id, :int],
276
239
  [:tick_type, :int],
277
240
  [:price, :decimal],
278
241
  [:size, :int],
279
242
  [:can_auto_execute, :int]
280
243
 
281
- TickSize = def_message 2, AbstractTick,
282
- [:id, :int], # ticker_id
244
+ TickSize = def_message [2, 6], AbstractTick,
245
+ [:ticker_id, :int],
283
246
  [:tick_type, :int],
284
247
  [:size, :int]
285
248
 
286
249
  TickGeneric = def_message 45, AbstractTick,
287
- [:id, :int], # ticker_id
250
+ [:ticker_id, :int],
288
251
  [:tick_type, :int],
289
252
  [:value, :decimal]
290
253
 
291
- TickString = def_message 46, AbstractTick,
292
- [:id, :int], # ticker_id
254
+ TickString = def_message [46, 6], AbstractTick,
255
+ [:ticker_id, :int],
293
256
  [:tick_type, :int],
294
257
  [:value, :string]
295
258
 
296
259
  TickEFP = def_message 47, AbstractTick,
297
- [:id, :int], # ticker_id
260
+ [:ticker_id, :int],
298
261
  [:tick_type, :int],
299
262
  [:basis_points, :decimal],
300
263
  [:formatted_basis_points, :string],
@@ -307,7 +270,7 @@ module IB
307
270
  # TWS�s option model volatilities, prices, and deltas, along with the present
308
271
  # value of dividends expected on that options underlier are received.
309
272
  # TickOption message contains following @data:
310
- # :id - Ticker Id that was specified previously in the call to reqMktData()
273
+ # :ticker_id - Id that was specified previously in the call to reqMktData()
311
274
  # :tick_type - Specifies the type of option computation (see TICK_TYPES).
312
275
  # :implied_volatility - The implied volatility calculated by the TWS option
313
276
  # modeler, using the specified :tick_type value.
@@ -318,42 +281,27 @@ module IB
318
281
  # :vega - The option vega value.
319
282
  # :theta - The option theta value.
320
283
  # :under_price - The price of the underlying.
321
- class TickOption < AbstractTick
322
- @message_id = 21
323
-
324
- # Read @data[key] if it was computed (received value above limit)
325
- # Leave @data[key] nil if received value below limit ("not yet computed")
326
- def read_computed key, limit
327
- value = @socket.read_decimal
328
- # limit is the "not yet computed" indicator
329
- @data[key] = value <= limit ? nil : value
330
- end
331
-
332
- def load
333
- super
334
-
335
- @data[:id] = @socket.read_int # ticker_id
336
- @data[:tick_type] = @socket.read_int
337
- read_computed :implied_volatility, -1 #-1 is the "not yet computed" indicator
338
- read_computed :delta, -2 # -2 is the "not yet computed" indicator
339
- read_computed :option_price, -1 # -1 is the "not yet computed" indicator
340
- read_computed :pv_dividend, -1 # -1 is the "not yet computed" indicator
341
- read_computed :gamma, -2 # -2 is the "not yet computed" indicator
342
- read_computed :vega, -2 # -2 is the "not yet computed" indicator
343
- read_computed :theta, -2 # -2 is the "not yet computed" indicator
344
- read_computed :under_price, -1 # -1 is the "not yet computed" indicator
345
- end
346
-
347
- def to_human
348
- "<TickOption #{type} for #{:id}: underlying @ #{under_price}, "+
349
- "option @ #{option_price}, IV #{implied_volatility}%, delta #{delta}, " +
350
- "gamma #{gamma}, vega #{vega}, theta #{theta}, pv_dividend #{pv_dividend}>"
351
- end
352
- end # TickOption
353
- TickOptionComputation = TickOption
284
+ TickOptionComputation = TickOption =
285
+ def_message([21, 6], AbstractTick,
286
+ [:ticker_id, :int],
287
+ [:tick_type, :int],
288
+ # What is the "not yet computed" indicator:
289
+ [:implied_volatility, :decimal_limit_1], # -1 and below
290
+ [:delta, :decimal_limit_2], # -2 and below
291
+ [:option_price, :decimal_limit_1], # -1 -"-
292
+ [:pv_dividend, :decimal_limit_1], # -1 -"-
293
+ [:gamma, :decimal_limit_2], # -2 -"-
294
+ [:vega, :decimal_limit_2], # -2 -"-
295
+ [:theta, :decimal_limit_2], # -2 -"-
296
+ [:under_price, :decimal_limit_1]) do
297
+
298
+ "<TickOption #{type} for #{:ticker_id}: underlying @ #{under_price}, "+
299
+ "option @ #{option_price}, IV #{implied_volatility}%, delta #{delta}, " +
300
+ "gamma #{gamma}, vega #{vega}, theta #{theta}, pv_dividend #{pv_dividend}>"
301
+ end
354
302
 
355
303
  MarketDepth =
356
- def_message 12, [:id, :int],
304
+ def_message 12, [:request_id, :int],
357
305
  [:position, :int], # The row Id of this market depth entry.
358
306
  [:operation, :int], # How it should be applied to the market depth:
359
307
  # 0 = insert this new order into the row identified by :position
@@ -363,7 +311,6 @@ module IB
363
311
  [:price, :decimal],
364
312
  [:size, :int]
365
313
  class MarketDepth
366
-
367
314
  def side
368
315
  @data[:side] == 0 ? :ask : :bid
369
316
  end
@@ -373,28 +320,27 @@ module IB
373
320
  end
374
321
 
375
322
  def to_human
376
- "<#{self.class.to_s.split(/::/).last}: #{operation} #{side} @ "+
323
+ "<#{self.message_type}: #{operation} #{side} @ "+
377
324
  "#{position} = #{price} x #{size}>"
378
325
  end
379
326
  end
380
327
 
381
328
  MarketDepthL2 =
382
- def_message 13, MarketDepth,
383
- [:id, :int],
384
- [:position, :int], # The row Id of this market depth entry.
329
+ def_message 13, MarketDepth, # Fields descriptions - see above
330
+ [:request_id, :int],
331
+ [:position, :int],
385
332
  [:market_maker, :string], # The exchange hosting this order.
386
- [:operation, :int], # How it should be applied to the market depth:
387
- # 0 = insert this new order into the row identified by :position
388
- # 1 = update the existing order in the row identified by :position
389
- # 2 = delete the existing order at the row identified by :position
390
- [:side, :int], # side of the book: 0 = ask, 1 = bid
333
+ [:operation, :int],
334
+ [:side, :int],
391
335
  [:price, :decimal],
392
336
  [:size, :int]
393
337
 
394
338
  # Called Error in Java code, but in fact this type of messages also
395
339
  # deliver system alerts and additional (non-error) info from TWS.
396
- # It has additional accessors: #code and #message, derived from @data
397
- Alert = def_message 4, [:id, :int], [:code, :int], [:message, :string]
340
+ ErrorMessage = Error = Alert = def_message([4, 2],
341
+ [:error_id, :int],
342
+ [:code, :int],
343
+ [:message, :string])
398
344
  class Alert
399
345
  # Is it an Error message?
400
346
  def error?
@@ -412,347 +358,202 @@ module IB
412
358
  end
413
359
 
414
360
  def to_human
415
- "TWS #{ error? ? 'Error' : system? ? 'System' : 'Warning'
416
- } Message #{code}: #{message}"
361
+ "TWS #{ error? ? 'Error' : system? ? 'System' : 'Warning'} #{code}: #{message}"
417
362
  end
418
363
  end # class Alert
419
- Error = Alert
420
- ErrorMessage = Alert
421
-
422
- class OpenOrder < AbstractMessage
423
- @message_id = 5
424
-
425
- # TODO: Add id accessor to unify with OrderStatus message
426
- attr_accessor :order, :contract
427
-
428
- def load
429
- super
430
-
431
- @order = Models::Order.new :id => @socket.read_int
432
-
433
- @contract = Models::Contract.build :con_id => @socket.read_string,
434
- :symbol => @socket.read_string,
435
- :sec_type => @socket.read_string,
436
- :expiry => @socket.read_string,
437
- :strike => @socket.read_decimal,
438
- :right => @socket.read_string,
439
- :exchange => @socket.read_string,
440
- :currency => @socket.read_string,
441
- :local_symbol => @socket.read_string
442
-
443
- @order.action = @socket.read_string
444
- @order.total_quantity = @socket.read_int
445
- @order.order_type = @socket.read_string
446
- @order.limit_price = @socket.read_decimal
447
- @order.aux_price = @socket.read_decimal
448
- @order.tif = @socket.read_string
449
- @order.oca_group = @socket.read_string
450
- @order.account = @socket.read_string
451
- @order.open_close = @socket.read_string
452
- @order.origin = @socket.read_int
453
- @order.order_ref = @socket.read_string
454
- @order.client_id = @socket.read_int
455
- @order.perm_id = @socket.read_int
456
- @order.outside_rth = (@socket.read_int == 1)
457
- @order.hidden = (@socket.read_int == 1)
458
- @order.discretionary_amount = @socket.read_decimal
459
- @order.good_after_time = @socket.read_string
460
- @socket.read_string # skip deprecated sharesAllocation field
461
-
462
- @order.fa_group = @socket.read_string
463
- @order.fa_method = @socket.read_string
464
- @order.fa_percentage = @socket.read_string
465
- @order.fa_profile = @socket.read_string
466
- @order.good_till_date = @socket.read_string
467
- @order.rule_80a = @socket.read_string
468
- @order.percent_offset = @socket.read_decimal
469
- @order.settling_firm = @socket.read_string
470
- @order.short_sale_slot = @socket.read_int
471
- @order.designated_location = @socket.read_string
472
- @order.exempt_code = @socket.read_int # skipped in ver 51?
473
- @order.auction_strategy = @socket.read_int
474
- @order.starting_price = @socket.read_decimal
475
- @order.stock_ref_price = @socket.read_decimal
476
- @order.delta = @socket.read_decimal
477
- @order.stock_range_lower = @socket.read_decimal
478
- @order.stock_range_upper = @socket.read_decimal
479
- @order.display_size = @socket.read_int
480
- #@order.rth_only = @socket.read_boolean
481
- @order.block_order = @socket.read_boolean
482
- @order.sweep_to_fill = @socket.read_boolean
483
- @order.all_or_none = @socket.read_boolean
484
- @order.min_quantity = @socket.read_int
485
- @order.oca_type = @socket.read_int
486
- @order.etrade_only = @socket.read_boolean
487
- @order.firm_quote_only = @socket.read_boolean
488
- @order.nbbo_price_cap = @socket.read_decimal
489
- @order.parent_id = @socket.read_int
490
- @order.trigger_method = @socket.read_int
491
- @order.volatility = @socket.read_decimal
492
- @order.volatility_type = @socket.read_int
493
- @order.delta_neutral_order_type = @socket.read_string
494
- @order.delta_neutral_aux_price = @socket.read_decimal
495
-
496
- @order.continuous_update = @socket.read_int
497
- @order.reference_price_type = @socket.read_int
498
- @order.trail_stop_price = @socket.read_decimal
499
- @order.basis_points = @socket.read_decimal
500
- @order.basis_points_type = @socket.read_int
501
- @contract.legs_description = @socket.read_string
502
- @order.scale_init_level_size = @socket.read_int_max
503
- @order.scale_subs_level_size = @socket.read_int_max
504
- @order.scale_price_increment = @socket.read_decimal_max
505
- @order.clearing_account = @socket.read_string
506
- @order.clearing_intent = @socket.read_string
507
- @order.not_held = (@socket.read_int == 1)
508
-
509
- under_comp_present = (@socket.read_int == 1)
510
-
511
- if under_comp_present
512
- @contract.under_comp = true
513
- @contract.under_con_id = @socket.read_int
514
- @contract.under_delta = @socket.read_decimal
515
- @contract.under_price = @socket.read_decimal
516
- end
517
-
518
- @order.algo_strategy = @socket.read_string
519
-
520
- unless @order.algo_strategy.nil? || @order.algo_strategy.empty?
521
- algo_params_count = @socket.read_int
522
- if algo_params_count > 0
523
- @order.algo_params = Hash.new
524
- algo_params_count.times do
525
- tag = @socket.read_string
526
- value = @socket.read_string
527
- @order.algo_params[tag] = value
528
- end
529
- end
530
- end
531
-
532
- @order.what_if = (@socket.read_int == 1)
533
- @order.status = @socket.read_string
534
- @order.init_margin = @socket.read_string
535
- @order.maint_margin = @socket.read_string
536
- @order.equity_with_loan = @socket.read_string
537
- @order.commission = @socket.read_decimal_max # May be nil!
538
- @order.min_commission = @socket.read_decimal_max # May be nil!
539
- @order.max_commission = @socket.read_decimal_max # May be nil!
540
- @order.commission_currency = @socket.read_string
541
- @order.warning_text = @socket.read_string
542
- end
543
-
544
- def to_human
545
- "<OpenOrder: #{@contract.to_human} #{@order.to_human}>"
546
- end
547
- end # OpenOrder
548
-
549
- class PortfolioValue < AbstractMessage
550
- @message_id = 7
551
364
 
552
- attr_accessor :contract
365
+ PortfolioValue = def_message [7, 7],
366
+ [:contract, :con_id, :int],
367
+ [:contract, :symbol, :string],
368
+ [:contract, :sec_type, :string],
369
+ [:contract, :expiry, :string],
370
+ [:contract, :strike, :decimal],
371
+ [:contract, :right, :string],
372
+ [:contract, :multiplier, :string],
373
+ [:contract, :primary_exchange, :string],
374
+ [:contract, :currency, :string],
375
+ [:contract, :local_symbol, :string],
376
+ [:position, :int],
377
+ [:market_price, :decimal],
378
+ [:market_value, :decimal],
379
+ [:average_cost, :decimal],
380
+ [:unrealized_pnl, :decimal_max], # May be nil!
381
+ [:realized_pnl, :decimal_max], # May be nil!
382
+ [:account_name, :string]
383
+ class PortfolioValue
553
384
 
554
385
  def load
555
386
  super
556
-
557
- @contract = Models::Contract.build :con_id => @socket.read_int,
558
- :symbol => @socket.read_string,
559
- :sec_type => @socket.read_string,
560
- :expiry => @socket.read_string,
561
- :strike => @socket.read_decimal,
562
- :right => @socket.read_string,
563
- :multiplier => @socket.read_string,
564
- :primary_exchange => @socket.read_string,
565
- :currency => @socket.read_string,
566
- :local_symbol => @socket.read_string
567
- load_map [:position, :int],
568
- [:market_price, :decimal],
569
- [:market_value, :decimal],
570
- [:average_cost, :decimal],
571
- [:unrealized_pnl, :decimal_max], # May be nil!
572
- [:realized_pnl, :decimal_max], # May be nil!
573
- [:account_name, :string]
387
+ @contract = Models::Contract.build @data[:contract]
574
388
  end
575
389
 
576
390
  def to_human
577
- "<PortfolioValue: #{@contract.to_human} (#{position}): Market #{market_price}" +
391
+ "<PortfolioValue: #{contract.to_human} (#{position}): Market #{market_price}" +
578
392
  " price #{market_value} value; PnL: #{unrealized_pnl} unrealized," +
579
393
  " #{realized_pnl} realized; account #{account_name}>"
580
394
  end
581
-
582
395
  end # PortfolioValue
583
396
 
584
- class ContractData < AbstractMessage
585
- @message_id = 10
586
-
587
- attr_accessor :contract
588
-
397
+ ContractDetails = ContractData =
398
+ def_message([10, 6],
399
+ [:request_id, :int], # request id
400
+ [:contract, :symbol, :string],
401
+ [:contract, :sec_type, :string],
402
+ [:contract, :expiry, :string],
403
+ [:contract, :strike, :decimal],
404
+ [:contract, :right, :string],
405
+ [:contract, :exchange, :string],
406
+ [:contract, :currency, :string],
407
+ [:contract, :local_symbol, :string],
408
+
409
+ [:contract, :market_name, :string], # extended
410
+ [:contract, :trading_class, :string],
411
+ [:contract, :con_id, :int],
412
+ [:contract, :min_tick, :decimal],
413
+ [:contract, :multiplier, :string],
414
+ [:contract, :order_types, :string],
415
+ [:contract, :valid_exchanges, :string],
416
+ [:contract, :price_magnifier, :int],
417
+ [:contract, :under_con_id, :int],
418
+ [:contract, :long_name, :string],
419
+ [:contract, :primary_exchange, :string],
420
+ [:contract, :contract_month, :string],
421
+ [:contract, :industry, :string],
422
+ [:contract, :category, :string],
423
+ [:contract, :subcategory, :string],
424
+ [:contract, :time_zone, :string],
425
+ [:contract, :trading_hours, :string],
426
+ [:contract, :liquid_hours, :string])
427
+
428
+ class ContractData
589
429
  def load
590
430
  super
591
- load_map [:id, :int] # request id
592
-
593
- @contract =
594
- Models::Contract.build :symbol => @socket.read_string,
595
- :sec_type => @socket.read_string,
596
- :expiry => @socket.read_string,
597
- :strike => @socket.read_decimal,
598
- :right => @socket.read_string,
599
- :exchange => @socket.read_string,
600
- :currency => @socket.read_string,
601
- :local_symbol => @socket.read_string,
602
-
603
- :market_name => @socket.read_string,
604
- :trading_class => @socket.read_string,
605
- :con_id => @socket.read_int,
606
- :min_tick => @socket.read_decimal,
607
- :multiplier => @socket.read_string,
608
- :order_types => @socket.read_string,
609
- :valid_exchanges => @socket.read_string,
610
- :price_magnifier => @socket.read_int,
611
-
612
- :under_con_id => @socket.read_int,
613
- :long_name => @socket.read_string,
614
- :primary_exchange => @socket.read_string,
615
- :contract_month => @socket.read_string,
616
- :industry => @socket.read_string,
617
- :category => @socket.read_string,
618
- :subcategory => @socket.read_string,
619
- :time_zone => @socket.read_string,
620
- :trading_hours => @socket.read_string,
621
- :liquid_hours => @socket.read_string
431
+ @contract = Models::Contract.build @data[:contract]
622
432
  end
623
433
  end # ContractData
624
- ContractDetails = ContractData
625
-
626
- class ExecutionData < AbstractMessage
627
- @message_id = 11
628
-
629
- attr_accessor :contract, :execution
630
434
 
435
+ ExecutionData =
436
+ def_message [11, 7],
437
+ # The reqID that was specified previously in the call to reqExecution()
438
+ [:request_id, :int],
439
+ [:execution, :order_id, :int],
440
+ [:contract, :con_id, :int],
441
+ [:contract, :symbol, :string],
442
+ [:contract, :sec_type, :string],
443
+ [:contract, :expiry, :string],
444
+ [:contract, :strike, :decimal],
445
+ [:contract, :right, :string],
446
+ [:contract, :exchange, :string],
447
+ [:contract, :currency, :string],
448
+ [:contract, :local_symbol, :string],
449
+
450
+ [:execution, :exec_id, :string], # Weird format
451
+ [:execution, :time, :string],
452
+ [:execution, :account_name, :string],
453
+ [:execution, :exchange, :string],
454
+ [:execution, :side, :string],
455
+ [:execution, :shares, :int],
456
+ [:execution, :price, :decimal],
457
+ [:execution, :perm_id, :int],
458
+ [:execution, :client_id, :int],
459
+ [:execution, :liquidation, :int],
460
+ [:execution, :cumulative_quantity, :int],
461
+ [:execution, :average_price, :decimal]
462
+
463
+ class ExecutionData
631
464
  def load
632
465
  super
633
- load_map [:id, :int], # request_id
634
- [:order_id, :int]
635
-
636
- @contract =
637
- Models::Contract.build :con_id => @socket.read_int,
638
- :symbol => @socket.read_string,
639
- :sec_type => @socket.read_string,
640
- :expiry => @socket.read_string,
641
- :strike => @socket.read_decimal,
642
- :right => @socket.read_string,
643
- :exchange => @socket.read_string,
644
- :currency => @socket.read_string,
645
- :local_symbol => @socket.read_string
646
- @execution =
647
- Models::Execution.new :order_id => @data[:order_id],
648
- :exec_id => @socket.read_string,
649
- :time => @socket.read_string,
650
- :account_number => @socket.read_string,
651
- :exchange => @socket.read_string,
652
- :side => @socket.read_string,
653
- :shares => @socket.read_int,
654
- :price => @socket.read_decimal,
655
- :perm_id => @socket.read_int,
656
- :client_id => @socket.read_int,
657
- :liquidation => @socket.read_int,
658
- :cumulative_quantity => @socket.read_int,
659
- :average_price => @socket.read_decimal
466
+ @contract = Models::Contract.build @data[:contract]
467
+ @execution = Models::Execution.new @data[:execution]
660
468
  end
661
469
 
662
470
  def to_human
663
- "<ExecutionData: #{contract.to_human}, #{execution}>"
471
+ "<ExecutionData #{request_id}: #{contract.to_human}, #{execution}>"
664
472
  end
665
473
  end # ExecutionData
666
474
 
667
- # HistoricalData contains following @data:
668
- # General:
669
- # :id - The ID of the request to which this is responding
670
- # :count - Number of Historical data points returned (size of :results).
671
- # :results - an Array of Historical Data Bars
672
- # :start_date - beginning of returned Historical data period
673
- # :end_date - end of returned Historical data period
674
- # Each returned Bar in @data[:results] Array contains this data:
675
- # :date - The date-time stamp of the start of the bar. The format is
676
- # determined by the RequestHistoricalData formatDate parameter.
677
- # :open - The bar opening price.
678
- # :high - The high price during the time covered by the bar.
679
- # :low - The low price during the time covered by the bar.
680
- # :close - The bar closing price.
681
- # :volume - The volume during the time covered by the bar.
682
- # :trades - When TRADES historical data is returned, represents number of trades
683
- # that occurred during the time period the bar covers
684
- # :wap - The weighted average price during the time covered by the bar.
685
- # :has_gaps - Whether or not there are gaps in the data.
686
- class HistoricalData < AbstractMessage
687
- @message_id = 17
688
-
475
+ BondContractData =
476
+ def_message [18, 4],
477
+ [:request_id, :int], # request id
478
+ [:contract, :symbol, :string],
479
+ [:contract, :sec_type, :string],
480
+ [:contract, :cusip, :string],
481
+ [:contract, :coupon, :decimal],
482
+ [:contract, :maturity, :string],
483
+ [:contract, :issue_date, :string],
484
+ [:contract, :ratings, :string],
485
+ [:contract, :bond_type, :string],
486
+ [:contract, :coupon_type, :string],
487
+ [:contract, :convertible, :boolean],
488
+ [:contract, :callable, :boolean],
489
+ [:contract, :puttable, :boolean],
490
+ [:contract, :desc_append, :string],
491
+ [:contract, :exchange, :string],
492
+ [:contract, :currency, :string],
493
+ [:contract, :market_name, :string], # extended
494
+ [:contract, :trading_class, :string],
495
+ [:contract, :con_id, :int],
496
+ [:contract, :min_tick, :decimal],
497
+ [:contract, :order_types, :string],
498
+ [:contract, :valid_exchanges, :string],
499
+ [:contract, :valid_next_option_date, :string],
500
+ [:contract, :valid_next_option_type, :string],
501
+ [:contract, :valid_next_option_partial, :string],
502
+ [:contract, :notes, :string],
503
+ [:contract, :long_name, :string]
504
+
505
+ class BondContractData
689
506
  def load
690
507
  super
691
- load_map [:id, :int],
692
- [:start_date, :string],
693
- [:end_date, :string],
694
- [:count, :int]
695
-
696
- @data[:results] = Array.new(@data[:count]) do |index|
697
- Models::Bar.new :date => @socket.read_string,
698
- :open => @socket.read_decimal,
699
- :high => @socket.read_decimal,
700
- :low => @socket.read_decimal,
701
- :close => @socket.read_decimal,
702
- :volume => @socket.read_int,
703
- :wap => @socket.read_decimal,
704
- :has_gaps => @socket.read_string,
705
- :trades => @socket.read_int
706
- end
508
+ @contract = Models::Contract.build @data[:contract]
707
509
  end
510
+ end # BondContractData
708
511
 
709
- def to_human
710
- "<HistoricalData: req: #{id}, #{item_count} items, #{start_date} to #{end_date}>"
512
+ # The server sends this message upon accepting a Delta-Neutral DN RFQ
513
+ # - see API Reference p. 26
514
+ DeltaNeutralValidation = def_message 56,
515
+ [:request_id, :int],
516
+ [:contract, :under_con_id, :int],
517
+ [:contract, :under_delta, :decimal],
518
+ [:contract, :under_price, :decimal]
519
+ class DeltaNeutralValidation
520
+ def load
521
+ super
522
+ @contract = Models::Contract.build @data[:contract].merge(:under_comp => true)
711
523
  end
712
- end # HistoricalData
713
-
714
- class BondContractData < AbstractMessage
715
- @message_id = 18
716
-
717
- attr_accessor :contract
524
+ end # DeltaNeutralValidation
718
525
 
526
+ # RealTimeBar contains following @data:
527
+ # :request_id - The ID of the *request* to which this is responding
528
+ # :time - The date-time stamp of the start of the bar. The format is offset in
529
+ # seconds from the beginning of 1970, same format as the UNIX epoch time
530
+ # :bar - received RT Bar
531
+ RealTimeBar = def_message 50,
532
+ [:request_id, :int],
533
+ [:bar, :time, :int],
534
+ [:bar, :open, :decimal],
535
+ [:bar, :high, :decimal],
536
+ [:bar, :low, :decimal],
537
+ [:bar, :close, :decimal],
538
+ [:bar, :volume, :int],
539
+ [:bar, :wap, :decimal],
540
+ [:bar, :trades, :int]
541
+ class RealTimeBar
719
542
  def load
720
543
  super
721
- load_map [:id, :int] # request id
722
-
723
- @contract =
724
- Models::Contract.build :symbol => @socket.read_string,
725
- :sec_type => @socket.read_string,
726
- :cusip => @socket.read_string,
727
- :coupon => @socket.read_decimal,
728
- :maturity => @socket.read_string,
729
- :issue_date => @socket.read_string,
730
- :ratings => @socket.read_string,
731
- :bond_type => @socket.read_string,
732
- :coupon_type => @socket.read_string,
733
- :convertible => @socket.read_boolean,
734
- :callable => @socket.read_boolean,
735
- :puttable => @socket.read_boolean,
736
- :desc_append => @socket.read_string,
737
- :exchange => @socket.read_string,
738
- :currency => @socket.read_string,
739
- :market_name => @socket.read_string,
740
- :trading_class => @socket.read_string,
741
- :con_id => @socket.read_int,
742
- :min_tick => @socket.read_decimal,
743
- :order_types => @socket.read_string,
744
- :valid_exchanges => @socket.read_string,
745
- :valid_next_option_date => @socket.read_string,
746
- :valid_next_option_type => @socket.read_string,
747
- :valid_next_option_partial => @socket.read_string,
748
- :notes => @socket.read_string,
749
- :long_name => @socket.read_string
544
+ @bar = Models::Bar.new @data[:bar]
750
545
  end
751
- end # BondContractData
546
+
547
+ def to_human
548
+ "<RealTimeBar: #{request_id} #{time}, #{bar}>"
549
+ end
550
+ end # RealTimeBar
551
+
552
+ ### Messages with really complicated message loading logics (cycles, conditions)
752
553
 
753
554
  # This method receives the requested market scanner data results.
754
555
  # ScannerData contains following @data:
755
- # :id - The ID of the request to which this row is responding
556
+ # :request_id - The ID of the request to which this row is responding
756
557
  # :count - Number of data points returned (size of :results).
757
558
  # :results - an Array of Hashes, each hash contains a set of
758
559
  # data about one scanned Contract:
@@ -761,15 +562,16 @@ module IB
761
562
  # :benchmark - Varies based on query.
762
563
  # :projection - Varies based on query.
763
564
  # :legs - Describes combo legs when scan is returning EFP.
764
- class ScannerData < AbstractMessage
765
- @message_id = 20
565
+ ScannerData = def_message [20, 3],
566
+ [:request_id, :int], # request id
567
+ [:count, :int]
568
+ class ScannerData
569
+ attr_accessor :results
766
570
 
767
571
  def load
768
572
  super
769
- load_map [:id, :int],
770
- [:count, :int]
771
573
 
772
- @data[:results] = Array.new(@data[:count]) do |index|
574
+ @results = Array.new(@data[:count]) do |index|
773
575
  {:rank => @socket.read_int,
774
576
  :contract => Contract.build(:con_id => @socket.read_int,
775
577
  :symbol => @socket.read_str,
@@ -787,63 +589,189 @@ module IB
787
589
  :projection => @socket.read_str,
788
590
  :legs => @socket.read_str,
789
591
  }
790
- #eWrapper().scannerData(tickerId, rank, contract, distance,
791
- # benchmark, projection, legsStr);
792
-
793
592
  end
794
-
795
- #eWrapper().scannerDataEnd(tickerId);
796
593
  end
797
594
  end # ScannerData
798
595
 
799
596
  # HistoricalData contains following @data:
800
- # :id - The ID of the *request* to which this is responding
801
- # :time - The date-time stamp of the start of the bar. The format is offset in
802
- # seconds from the beginning of 1970, same format as the UNIX epoch time
803
- # :bar - received RT Bar
804
- class RealTimeBar < AbstractMessage
805
- @message_id = 50
597
+ # General:
598
+ # :request_id - The ID of the request to which this is responding
599
+ # :count - Number of Historical data points returned (size of :results).
600
+ # :results - an Array of Historical Data Bars
601
+ # :start_date - beginning of returned Historical data period
602
+ # :end_date - end of returned Historical data period
603
+ # Each returned Bar in @data[:results] Array contains this data:
604
+ # :date - The date-time stamp of the start of the bar. The format is
605
+ # determined by the RequestHistoricalData formatDate parameter.
606
+ # :open - The bar opening price.
607
+ # :high - The high price during the time covered by the bar.
608
+ # :low - The low price during the time covered by the bar.
609
+ # :close - The bar closing price.
610
+ # :volume - The volume during the time covered by the bar.
611
+ # :trades - When TRADES historical data is returned, represents number of trades
612
+ # that occurred during the time period the bar covers
613
+ # :wap - The weighted average price during the time covered by the bar.
614
+ # :has_gaps - Whether or not there are gaps in the data.
806
615
 
807
- attr_accessor :bar
616
+ HistoricalData = def_message [17, 3],
617
+ [:request_id, :int],
618
+ [:start_date, :string],
619
+ [:end_date, :string],
620
+ [:count, :int]
621
+ class HistoricalData
622
+ attr_accessor :results
808
623
 
809
624
  def load
810
625
  super
811
- load_map [:id, :int],
812
- [:time, :int] # long!
813
-
814
- @bar = Models::Bar.new :date => Time.at(@data[:time]),
815
- :open => @socket.read_decimal,
816
- :high => @socket.read_decimal,
817
- :low => @socket.read_decimal,
818
- :close => @socket.read_decimal,
819
- :volume => @socket.read_int,
820
- :wap => @socket.read_decimal,
821
- :trades => @socket.read_int
626
+
627
+ @results = Array.new(@data[:count]) do |index|
628
+ Models::Bar.new :time => @socket.read_string,
629
+ :open => @socket.read_decimal,
630
+ :high => @socket.read_decimal,
631
+ :low => @socket.read_decimal,
632
+ :close => @socket.read_decimal,
633
+ :volume => @socket.read_int,
634
+ :wap => @socket.read_decimal,
635
+ :has_gaps => @socket.read_string,
636
+ :trades => @socket.read_int
637
+ end
822
638
  end
823
639
 
824
640
  def to_human
825
- "<RealTimeBar: req: #{id}, #{bar}>"
641
+ "<HistoricalData: #{request_id}, #{count} items, #{start_date} to #{end_date}>"
826
642
  end
827
- end # RealTimeBar
828
- RealTimeBars = RealTimeBar
643
+ end # HistoricalData
829
644
 
830
- # The server sends this message upon accepting a Delta-Neutral DN RFQ
831
- # - see API Reference p. 26
832
- class DeltaNeutralValidation < AbstractMessage
833
- @message_id = 56
834
645
 
835
- attr_accessor :contract
646
+ OpenOrder =
647
+ def_message [5, 23],
648
+ # The reqID that was specified previously in the call to reqExecution()
649
+ [:order, :order_id, :int],
650
+
651
+ [:contract, :con_id, :int],
652
+ [:contract, :symbol, :string],
653
+ [:contract, :sec_type, :string],
654
+ [:contract, :expiry, :string],
655
+ [:contract, :strike, :decimal],
656
+ [:contract, :right, :string],
657
+ [:contract, :exchange, :string],
658
+ [:contract, :currency, :string],
659
+ [:contract, :local_symbol, :string],
660
+
661
+ [:order, :action, :string],
662
+ [:order, :total_quantity, :int],
663
+ [:order, :order_type, :string],
664
+ [:order, :limit_price, :decimal],
665
+ [:order, :aux_price, :decimal],
666
+ [:order, :tif, :string],
667
+ [:order, :oca_group, :string],
668
+ [:order, :account, :string],
669
+ [:order, :open_close, :string],
670
+ [:order, :origin, :int],
671
+ [:order, :order_ref, :string],
672
+ [:order, :client_id, :int],
673
+ [:order, :perm_id, :int],
674
+ [:order, :outside_rth, :boolean], # (@socket.read_int == 1)
675
+ [:order, :hidden, :boolean], # (@socket.read_int == 1)
676
+ [:order, :discretionary_amount, :decimal],
677
+ [:order, :good_after_time, :string],
678
+ [:skip, :string], # skip deprecated sharesAllocation field
679
+
680
+ [:order, :fa_group, :string],
681
+ [:order, :fa_method, :string],
682
+ [:order, :fa_percentage, :string],
683
+ [:order, :fa_profile, :string],
684
+ [:order, :good_till_date, :string],
685
+ [:order, :rule_80a, :string],
686
+ [:order, :percent_offset, :decimal],
687
+ [:order, :settling_firm, :string],
688
+ [:order, :short_sale_slot, :int],
689
+ [:order, :designated_location, :string],
690
+ [:order, :exempt_code, :int], # skipped in ver 51?
691
+ [:order, :auction_strategy, :int],
692
+ [:order, :starting_price, :decimal],
693
+ [:order, :stock_ref_price, :decimal],
694
+ [:order, :delta, :decimal],
695
+ [:order, :stock_range_lower, :decimal],
696
+ [:order, :stock_range_upper, :decimal],
697
+ [:order, :display_size, :int],
698
+ #@order.rth_only = @socket.read_boolean
699
+ [:order, :block_order, :boolean],
700
+ [:order, :sweep_to_fill, :boolean],
701
+ [:order, :all_or_none, :boolean],
702
+ [:order, :min_quantity, :int],
703
+ [:order, :oca_type, :int],
704
+ [:order, :etrade_only, :boolean],
705
+ [:order, :firm_quote_only, :boolean],
706
+ [:order, :nbbo_price_cap, :decimal],
707
+ [:order, :parent_id, :int],
708
+ [:order, :trigger_method, :int],
709
+ [:order, :volatility, :decimal],
710
+ [:order, :volatility_type, :int],
711
+ [:order, :delta_neutral_order_type, :string],
712
+ [:order, :delta_neutral_aux_price, :decimal],
713
+
714
+ [:order, :continuous_update, :int],
715
+ [:order, :reference_price_type, :int],
716
+ [:order, :trail_stop_price, :decimal],
717
+ [:order, :basis_points, :decimal],
718
+ [:order, :basis_points_type, :int],
719
+ [:contract, :legs_description, :string],
720
+ [:order, :scale_init_level_size, :int_max],
721
+ [:order, :scale_subs_level_size, :int_max],
722
+ [:order, :scale_price_increment, :decimal_max],
723
+ [:order, :clearing_account, :string],
724
+ [:order, :clearing_intent, :string],
725
+ [:order, :not_held, :boolean] # (@socket.read_int == 1)
726
+
727
+ class OpenOrder
836
728
 
837
729
  def load
838
730
  super
839
- load_map [:id, :int] # request id
840
731
 
841
- @contract = Models::Contract.build :under_comp => true,
842
- :under_con_id => @socket.read_int,
843
- :under_delta => @socket.read_decimal,
844
- :under_price => @socket.read_decimal
732
+ load_map [:contract, :under_comp, :boolean] # (@socket.read_int == 1)
733
+
734
+ if @data[:contract][:under_comp]
735
+ load_map [:contract, :under_con_id, :int],
736
+ [:contract, :under_delta, :decimal],
737
+ [:contract, :under_price, :decimal]
738
+ end
739
+
740
+ load_map [:order, :algo_strategy, :string]
741
+
742
+ unless @data[:order][:algo_strategy].nil? || @data[:order][:algo_strategy].empty?
743
+ load_map [:algo_params_count, :int]
744
+ if @data[:algo_params_count] > 0
745
+ @data[:order][:algo_params] = Hash.new
746
+ @data[:algo_params_count].times do
747
+ tag = @socket.read_string
748
+ value = @socket.read_string
749
+ @data[:order][:algo_params][tag] = value
750
+ end
751
+ end
752
+ end
753
+
754
+ load_map [:order, :what_if, :boolean], # (@socket.read_int == 1)
755
+ [:order, :status, :string],
756
+ [:order, :init_margin, :string],
757
+ [:order, :maint_margin, :string],
758
+ [:order, :equity_with_loan, :string],
759
+ [:order, :commission, :decimal_max], # May be nil!
760
+ [:order, :min_commission, :decimal_max], # May be nil!
761
+ [:order, :max_commission, :decimal_max], # May be nil!
762
+ [:order, :commission_currency, :string],
763
+ [:order, :warning_text, :string]
764
+
765
+ @order = Models::Order.new @data[:order]
766
+ @contract = Models::Contract.build @data[:contract]
845
767
  end
846
- end # DeltaNeutralValidation
768
+
769
+ def to_human
770
+ "<OpenOrder: #{@contract.to_human} #{@order.to_human}>"
771
+ end
772
+ end
773
+
774
+ # OpenOrder
847
775
 
848
776
  Table = Hash.new
849
777
  Classes.each { |msg_class| Table[msg_class.message_id] = msg_class }