ib-ruby 0.6.1 → 0.7.0

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/HISTORY +4 -0
  2. data/README.md +25 -17
  3. data/VERSION +1 -1
  4. data/bin/account_info +1 -1
  5. data/bin/cancel_orders +2 -1
  6. data/bin/contract_details +3 -2
  7. data/bin/depth_of_market +1 -1
  8. data/bin/historic_data +1 -1
  9. data/bin/historic_data_cli +57 -104
  10. data/bin/list_orders +4 -3
  11. data/bin/market_data +1 -1
  12. data/bin/option_data +1 -1
  13. data/bin/place_combo_order +63 -0
  14. data/bin/place_order +2 -4
  15. data/bin/template +1 -1
  16. data/bin/{generic_data.rb → tick_data} +3 -1
  17. data/bin/time_and_sales +1 -1
  18. data/lib/ib-ruby.rb +1 -0
  19. data/lib/ib-ruby/connection.rb +68 -68
  20. data/lib/ib-ruby/errors.rb +28 -0
  21. data/lib/ib-ruby/extensions.rb +7 -0
  22. data/lib/ib-ruby/messages.rb +1 -0
  23. data/lib/ib-ruby/messages/abstract_message.rb +16 -11
  24. data/lib/ib-ruby/messages/incoming.rb +125 -329
  25. data/lib/ib-ruby/messages/incoming/open_order.rb +193 -0
  26. data/lib/ib-ruby/messages/incoming/ticks.rb +131 -0
  27. data/lib/ib-ruby/messages/outgoing.rb +44 -45
  28. data/lib/ib-ruby/models/combo_leg.rb +16 -1
  29. data/lib/ib-ruby/models/contract.rb +18 -10
  30. data/lib/ib-ruby/models/contract/bag.rb +1 -7
  31. data/lib/ib-ruby/models/execution.rb +2 -1
  32. data/lib/ib-ruby/models/model.rb +1 -1
  33. data/lib/ib-ruby/models/order.rb +116 -56
  34. data/lib/ib-ruby/socket.rb +24 -3
  35. data/spec/account_helper.rb +2 -1
  36. data/spec/ib-ruby/messages/outgoing_spec.rb +1 -1
  37. data/spec/ib-ruby/models/combo_leg_spec.rb +0 -1
  38. data/spec/integration/account_info_spec.rb +2 -2
  39. data/spec/integration/contract_info_spec.rb +4 -4
  40. data/spec/integration/depth_data_spec.rb +3 -3
  41. data/spec/integration/historic_data_spec.rb +1 -1
  42. data/spec/integration/market_data_spec.rb +4 -4
  43. data/spec/integration/option_data_spec.rb +1 -1
  44. data/spec/integration/orders/combo_spec.rb +51 -0
  45. data/spec/integration/orders/execution_spec.rb +15 -8
  46. data/spec/integration/orders/placement_spec.rb +46 -72
  47. data/spec/integration/orders/valid_ids_spec.rb +6 -6
  48. data/spec/integration_helper.rb +0 -79
  49. data/spec/order_helper.rb +153 -0
  50. metadata +13 -4
@@ -6,3 +6,10 @@ class Time
6
6
  end
7
7
  end # Time
8
8
 
9
+ ### Patching Object#error in ib-ruby/errors
10
+ # def error message, type=:standard
11
+
12
+ ### Patching Object#log, #default_logger= in ib-ruby/logger
13
+ # def default_logger
14
+ # def default_logger= logger
15
+ # def log *args
@@ -8,4 +8,5 @@ module IB
8
8
 
9
9
  IncomingMessages = Messages::Incoming # Legacy alias
10
10
  OutgoingMessages = Messages::Outgoing # Legacy alias
11
+
11
12
  end
@@ -16,14 +16,12 @@ module IB
16
16
  # @message_type - Symbol: message type (e.g. :OpenOrderEnd)
17
17
  #
18
18
  # Instance attributes (at least):
19
- # version - int: current version of message format.
19
+ # @version - int: current version of message format.
20
20
  # @data - Hash of actual data read from a stream.
21
- #
22
- # Override the load(socket) method in your subclass to do actual reading into @data.
23
21
  class AbstractMessage
24
22
 
25
23
  # Class methods
26
- def self.data_map # Data keys (with types?)
24
+ def self.data_map # Map for converting between structured message and raw data
27
25
  @data_map ||= []
28
26
  end
29
27
 
@@ -61,16 +59,18 @@ module IB
61
59
 
62
60
  end # class AbstractMessage
63
61
 
64
- # Macro that defines short message classes using a one-liner
65
- # id_version is either a [message_id, version] pair or just message_id (version 1)
62
+ # Macro that defines short message classes using a one-liner.
63
+ # First arg is either a [message_id, version] pair or just message_id (version 1)
66
64
  # data_map contains instructions for processing @data Hash. Format:
67
65
  # Incoming messages: [field, type] or [group, field, type]
68
- # Outgoing messages: [field, default] or [field, method, [args]]
69
- def def_message id_version, *data_map, &to_human
66
+ # Outgoing messages: field, [field, default] or [field, method, [args]]
67
+ def def_message message_id_version, *data_map, &to_human
70
68
  base = data_map.first.is_a?(Class) ? data_map.shift : self::AbstractMessage
71
- Class.new(base) do
72
- @message_id, @version = id_version
73
- @version ||= 1
69
+ message_id, version = message_id_version
70
+
71
+ # Define new message class
72
+ message_class = Class.new(base) do
73
+ @message_id, @version = message_id, version || 1
74
74
  @data_map = data_map
75
75
 
76
76
  @data_map.each do |(name, _, type_args)|
@@ -83,6 +83,11 @@ module IB
83
83
 
84
84
  define_method(:to_human, &to_human) if to_human
85
85
  end
86
+
87
+ # Add defined message class to Classes Hash keyed by its message_id
88
+ self::Classes[message_id] = message_class
89
+
90
+ message_class
86
91
  end
87
92
 
88
93
  end # module Messages
@@ -11,85 +11,106 @@ require 'ib-ruby/messages/abstract_message'
11
11
  module IB
12
12
  module Messages
13
13
 
14
- # Incoming IB messages
14
+ # Incoming IB messages (received from TWS/Gateway)
15
15
  module Incoming
16
16
  extend Messages # def_message macros
17
17
 
18
- Classes = Array.new
18
+ # Container for specific message classes, keyed by their message_ids
19
+ Classes = {}
19
20
 
20
21
  class AbstractMessage < IB::Messages::AbstractMessage
21
22
 
22
- def self.inherited(by)
23
- super(by)
24
- Classes.push(by)
25
- end
26
-
27
23
  def version # Per message, received messages may have the different versions
28
24
  @data[:version]
29
25
  end
30
26
 
31
- # Read incoming message from given socket or instantiate with given data
32
- def initialize socket_or_data
27
+ def check_version actual, expected
28
+ unless actual == expected || expected.is_a?(Array) && expected.include?(actual)
29
+ error "Unsupported version #{actual} received, expected #{expected}"
30
+ end
31
+ end
32
+
33
+ # Create incoming message from a given source (IB server or data Hash)
34
+ def initialize source
33
35
  @created_at = Time.now
34
- if socket_or_data.is_a?(Hash)
35
- @data = socket_or_data
36
- else
37
- @data = {}
38
- @socket = socket_or_data
39
- self.load
40
- @socket = nil
36
+ if source[:socket] # Source is a server
37
+ @server = source
38
+ @data = Hash.new
39
+ begin
40
+ self.load
41
+ rescue => e
42
+ error "Reading #{self.class}: #{e.class}: #{e.message}", :load, e.backtrace
43
+ ensure
44
+ @server = nil
45
+ end
46
+ else # Source is a @data Hash
47
+ @data = source
41
48
  end
42
49
  end
43
50
 
51
+ def socket
52
+ @server[:socket]
53
+ end
54
+
44
55
  # Every message loads received message version first
56
+ # Override the load method in your subclass to do actual reading into @data.
45
57
  def load
46
- @data[:version] = @socket.read_int
58
+ @data[:version] = socket.read_int
47
59
 
48
- if @data[:version] != self.class.version
49
- raise "Unsupported version #{@data[:version]} of #{self.class} received"
50
- end
60
+ check_version @data[:version], self.class.version
51
61
 
52
62
  load_map *self.class.data_map
53
63
  end
54
64
 
55
- # Load @data from the socket according to the given map.
65
+ # Load @data from the socket according to the given data map.
56
66
  #
57
67
  # map is a series of Arrays in the format of
58
- # [ [ :name, :type ],
59
- # [ :group, :name, :type] ]
68
+ # [ :name, :type ], [ :group, :name, :type]
60
69
  # type identifiers must have a corresponding read_type method on socket (read_int, etc.).
61
70
  # group is used to lump together aggregates, such as Contract or Order fields
62
71
  def load_map(*map)
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
+ map.each do |instruction|
73
+ # We determine the function of the first element
74
+ head = instruction.first
75
+ case head
76
+ when Integer # >= Version condition: [ min_version, [map]]
77
+ load_map *instruction.drop(1) if version >= head
78
+
79
+ when Proc # Callable condition: [ condition, [map]]
80
+ load_map *instruction.drop(1) if head.call
81
+
82
+ when true # Pre-condition already succeeded!
83
+ load_map *instruction.drop(1)
84
+
85
+ when nil, false # Pre-condition already failed! Do nothing...
86
+
87
+ when Symbol # Normal map
88
+ group, name, type, block =
89
+ if instruction[2].nil? || instruction[2].is_a?(Proc)
90
+ [nil] + instruction # No group, [ :name, :type, (:block) ]
91
+ else
92
+ instruction # [ :group, :name, :type, (:block)]
93
+ end
94
+
95
+ data = socket.__send__("read_#{type}", &block)
96
+ if group
97
+ @data[group] ||= {}
98
+ @data[group][name] = data
99
+ else
100
+ @data[name] = data
101
+ end
102
+ else
103
+ error "Unrecognized instruction #{instruction}"
72
104
  end
73
105
  end
74
106
  end
75
- end # class AbstractMessage
76
-
77
- class AbstractTick < AbstractMessage
78
- # Returns Symbol with a meaningful name for received tick type
79
- def type
80
- TICK_TYPES[@data[:tick_type]]
81
- end
82
-
83
- def to_human
84
- "<#{self.message_type} #{type}:" +
85
- @data.map do |key, value|
86
- " #{key} #{value}" unless [:version, :ticker_id, :tick_type].include?(key)
87
- end.compact.join(',') + " >"
88
- end
89
107
  end
90
108
 
109
+ # class AbstractMessage
110
+
91
111
  ### Actual message classes (short definitions):
92
- #:status - String: Displays the order status. Possible values include:
112
+
113
+ # :status - String: Displays the order status. Possible values include:
93
114
  # � PendingSubmit - indicates that you have transmitted the order, but
94
115
  # have not yet received confirmation that it has been accepted by the
95
116
  # order destination. NOTE: This order status is NOT sent back by TWS
@@ -133,7 +154,6 @@ module IB
133
154
  " id/perm: #{order_id}/#{perm_id}>"
134
155
  end
135
156
 
136
-
137
157
  AccountValue = def_message([6, 2], [:key, :string],
138
158
  [:value, :string],
139
159
  [:currency, :string],
@@ -178,127 +198,25 @@ module IB
178
198
 
179
199
  # Receive Reuters global fundamental market data. There must be a subscription to
180
200
  # Reuters Fundamental set up in Account Management before you can receive this data.
181
- FundamentalData = def_message 50, [:request_id, :int], # request_id
182
- [:data, :string]
201
+ FundamentalData = def_message 50, [:request_id, :int], [:data, :string]
183
202
 
184
- ContractDataEnd = def_message 52, [:request_id, :int] # request_id
203
+ ContractDataEnd = def_message 52, [:request_id, :int]
185
204
 
186
205
  OpenOrderEnd = def_message 53
187
206
 
188
207
  AccountDownloadEnd = def_message 54, [:account_name, :string]
189
208
 
190
- ExecutionDataEnd = def_message 55, [:request_id, :int] # request_id
191
-
192
- TickSnapshotEnd = def_message 57, [:ticker_id, :int]
193
-
194
- ### Actual message classes (long definitions):
195
-
196
- # The IB code seems to dispatch up to two wrapped objects for this message, a tickPrice
197
- # and sometimes a tickSize, which seems to be identical to the TICK_SIZE object.
198
- #
199
- # Important note from
200
- # http://chuckcaplan.com/twsapi/index.php/void%20tickPrice%28%29 :
201
- #
202
- # "The low you get is NOT the low for the day as you'd expect it
203
- # to be. It appears IB calculates the low based on all
204
- # transactions after 4pm the previous day. The most inaccurate
205
- # results occur when the stock moves up in the 4-6pm aftermarket
206
- # on the previous day and then gaps open upward in the
207
- # morning. The low you receive from TWS can be easily be several
208
- # points different from the actual 9:30am-4pm low for the day in
209
- # cases like this. If you require a correct traded low for the
210
- # day, you can't get it from the TWS API. One possible source to
211
- # help build the right data would be to compare against what Yahoo
212
- # lists on finance.yahoo.com/q?s=ticker under the "Day's Range"
213
- # statistics (be careful here, because Yahoo will use anti-Denial
214
- # of Service techniques to hang your connection if you try to
215
- # request too many bytes in a short period of time from them). For
216
- # most purposes, a good enough approach would start by replacing
217
- # the TWS low for the day with Yahoo's day low when you first
218
- # start watching a stock ticker; let's call this time T. Then,
219
- # update your internal low if the bid or ask tick you receive is
220
- # lower than that for the remainder of the day. You should check
221
- # against Yahoo again at time T+20min to handle the occasional
222
- # case where the stock set a new low for the day in between
223
- # T-20min (the real time your original quote was from, taking into
224
- # account the delay) and time T. After that you should have a
225
- # correct enough low for the rest of the day as long as you keep
226
- # updating based on the bid/ask. It could still get slightly off
227
- # in a case where a short transaction setting a new low appears in
228
- # between ticks of data that TWS sends you. The high is probably
229
- # distorted in the same way the low is, which would throw your
230
- # results off if the stock traded after-hours and gapped down. It
231
- # should be corrected in a similar way as described above if this
232
- # is important to you."
233
- #
234
- # IB then emits at most 2 events on eWrapper:
235
- # tickPrice( tickerId, tickType, price, canAutoExecute)
236
- # tickSize( tickerId, sizeTickType, size)
237
- TickPrice = def_message [1, 6], AbstractTick,
238
- [:ticker_id, :int],
239
- [:tick_type, :int],
240
- [:price, :decimal],
241
- [:size, :int],
242
- [:can_auto_execute, :int]
243
-
244
- TickSize = def_message [2, 6], AbstractTick,
245
- [:ticker_id, :int],
246
- [:tick_type, :int],
247
- [:size, :int]
248
-
249
- TickGeneric = def_message [45, 6], AbstractTick,
250
- [:ticker_id, :int],
251
- [:tick_type, :int],
252
- [:value, :decimal]
253
-
254
- TickString = def_message [46, 6], AbstractTick,
255
- [:ticker_id, :int],
256
- [:tick_type, :int],
257
- [:value, :string]
258
-
259
- TickEFP = def_message [47, 6], AbstractTick,
260
- [:ticker_id, :int],
261
- [:tick_type, :int],
262
- [:basis_points, :decimal],
263
- [:formatted_basis_points, :string],
264
- [:implied_futures_price, :decimal],
265
- [:hold_days, :int],
266
- [:dividend_impact, :decimal],
267
- [:dividends_to_expiry, :decimal]
268
-
269
- # This message is received when the market in an option or its underlier moves.
270
- # TWS�s option model volatilities, prices, and deltas, along with the present
271
- # value of dividends expected on that options underlier are received.
272
- # TickOption message contains following @data:
273
- # :ticker_id - Id that was specified previously in the call to reqMktData()
274
- # :tick_type - Specifies the type of option computation (see TICK_TYPES).
275
- # :implied_volatility - The implied volatility calculated by the TWS option
276
- # modeler, using the specified :tick_type value.
277
- # :delta - The option delta value.
278
- # :option_price - The option price.
279
- # :pv_dividend - The present value of dividends expected on the options underlier
280
- # :gamma - The option gamma value.
281
- # :vega - The option vega value.
282
- # :theta - The option theta value.
283
- # :under_price - The price of the underlying.
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
209
+ ExecutionDataEnd = def_message 55, [:request_id, :int]
210
+
211
+ MarketDataType = def_message 58, [:request_id, :int], [:market_data_type, :int]
212
+
213
+ CommissionReport =
214
+ def_message 59, [:exec_id, :int],
215
+ [:commission, :decimal], # Commission amount.
216
+ [:currency, :int], # Commission currency
217
+ [:realized_pnl, :decimal],
218
+ [:yield, :decimal],
219
+ [:yield_redemption_date, :int]
302
220
 
303
221
  MarketDepth =
304
222
  def_message 12, [:request_id, :int],
@@ -433,7 +351,7 @@ module IB
433
351
  end # ContractData
434
352
 
435
353
  ExecutionData =
436
- def_message [11, 7],
354
+ def_message [11, 8],
437
355
  # The reqID that was specified previously in the call to reqExecution()
438
356
  [:request_id, :int],
439
357
  [:execution, :order_id, :int],
@@ -463,6 +381,11 @@ module IB
463
381
  class ExecutionData
464
382
  def load
465
383
  super
384
+
385
+ # As of client v.53, we can receive orderRef in ExecutionData
386
+ load_map [proc { | | @server[:client_version] >= 53 },
387
+ [:execution, :order_ref, :string]
388
+ ]
466
389
  @contract = Models::Contract.build @data[:contract]
467
390
  @execution = Models::Execution.new @data[:execution]
468
391
  end
@@ -470,11 +393,12 @@ module IB
470
393
  def to_human
471
394
  "<ExecutionData #{request_id}: #{contract.to_human}, #{execution}>"
472
395
  end
396
+
473
397
  end # ExecutionData
474
398
 
475
399
  BondContractData =
476
400
  def_message [18, 4],
477
- [:request_id, :int], # request id
401
+ [:request_id, :int],
478
402
  [:contract, :symbol, :string],
479
403
  [:contract, :sec_type, :string],
480
404
  [:contract, :cusip, :string],
@@ -571,23 +495,23 @@ module IB
571
495
  def load
572
496
  super
573
497
 
574
- @results = Array.new(@data[:count]) do |index|
575
- {:rank => @socket.read_int,
576
- :contract => Contract.build(:con_id => @socket.read_int,
577
- :symbol => @socket.read_str,
578
- :sec_type => @socket.read_str,
579
- :expiry => @socket.read_str,
580
- :strike => @socket.read_decimal,
581
- :right => @socket.read_str,
582
- :exchange => @socket.read_str,
583
- :currency => @socket.read_str,
584
- :local_symbol => @socket.read_str,
585
- :market_name => @socket.read_str,
586
- :trading_class => @socket.read_str),
587
- :distance => @socket.read_str,
588
- :benchmark => @socket.read_str,
589
- :projection => @socket.read_str,
590
- :legs => @socket.read_str,
498
+ @results = Array.new(@data[:count]) do |_|
499
+ {:rank => socket.read_int,
500
+ :contract => Contract.build(:con_id => socket.read_int,
501
+ :symbol => socket.read_str,
502
+ :sec_type => socket.read_str,
503
+ :expiry => socket.read_str,
504
+ :strike => socket.read_decimal,
505
+ :right => socket.read_str,
506
+ :exchange => socket.read_str,
507
+ :currency => socket.read_str,
508
+ :local_symbol => socket.read_str,
509
+ :market_name => socket.read_str,
510
+ :trading_class => socket.read_str),
511
+ :distance => socket.read_str,
512
+ :benchmark => socket.read_str,
513
+ :projection => socket.read_str,
514
+ :legs => socket.read_str,
591
515
  }
592
516
  end
593
517
  end
@@ -624,16 +548,16 @@ module IB
624
548
  def load
625
549
  super
626
550
 
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
551
+ @results = Array.new(@data[:count]) do |_|
552
+ Models::Bar.new :time => socket.read_string,
553
+ :open => socket.read_decimal,
554
+ :high => socket.read_decimal,
555
+ :low => socket.read_decimal,
556
+ :close => socket.read_decimal,
557
+ :volume => socket.read_int,
558
+ :wap => socket.read_decimal,
559
+ :has_gaps => socket.read_string,
560
+ :trades => socket.read_int
637
561
  end
638
562
  end
639
563
 
@@ -642,147 +566,17 @@ module IB
642
566
  end
643
567
  end # HistoricalData
644
568
 
645
-
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
728
-
729
- def load
730
- super
731
-
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]
767
- end
768
-
769
- def to_human
770
- "<OpenOrder: #{@contract.to_human} #{@order.to_human}>"
771
- end
772
- end
773
-
774
- # OpenOrder
775
-
776
- Table = Hash.new
777
- Classes.each { |msg_class| Table[msg_class.message_id] = msg_class }
778
-
779
569
  end # module Incoming
780
570
  end # module Messages
781
571
  end # module IB
782
- __END__
783
572
 
573
+ # Require standalone message source files
574
+ require 'ib-ruby/messages/incoming/ticks'
575
+ require 'ib-ruby/messages/incoming/open_order'
576
+
577
+ __END__
784
578
  // incoming msg id's
785
- static final int TICK_PRICE = 1; * TODO: realize both events
579
+ static final int TICK_PRICE = 1; *
786
580
  static final int TICK_SIZE = 2; *
787
581
  static final int ORDER_STATUS = 3; *
788
582
  static final int ERR_MSG = 4; *
@@ -792,7 +586,7 @@ __END__
792
586
  static final int ACCT_UPDATE_TIME = 8; *
793
587
  static final int NEXT_VALID_ID = 9; *
794
588
  static final int CONTRACT_DATA = 10; *
795
- static final int EXECUTION_DATA = 11; *
589
+ static final int EXECUTION_DATA = 11; ?
796
590
  static final int MARKET_DEPTH = 12; *
797
591
  static final int MARKET_DEPTH_L2 = 13; *
798
592
  static final int NEWS_BULLETINS = 14; *
@@ -815,3 +609,5 @@ __END__
815
609
  static final int EXECUTION_DATA_END = 55; *
816
610
  static final int DELTA_NEUTRAL_VALIDATION = 56; *
817
611
  static final int TICK_SNAPSHOT_END = 57; *
612
+ static final int MARKET_DATA_TYPE = 58; ?
613
+ static final int COMMISSION_REPORT = 59; ?