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.
@@ -8,29 +10,15 @@
8
10
  module IB
9
11
  module Messages
10
12
  module Outgoing
13
+ extend Messages # def_message macros
11
14
 
12
- class AbstractMessage
13
- # Class methods
14
- def self.message_id
15
- @message_id
16
- end
17
-
18
- def self.version
19
- @version
20
- end
21
-
22
- attr_reader :created_at, :data
15
+ class AbstractMessage < IB::Messages::AbstractMessage
23
16
 
24
- # data is a Hash
25
- def initialize(data = {})
17
+ def initialize data={}
26
18
  @data = data
27
19
  @created_at = Time.now
28
20
  end
29
21
 
30
- def to_human
31
- self.inspect
32
- end
33
-
34
22
  # This causes the message to send itself over the server socket in server[:socket].
35
23
  # "server" is the @server instance variable from the IB object.
36
24
  # You can also use this to e.g. get the server version number.
@@ -46,31 +34,37 @@ module IB
46
34
  datum = "1" if datum == true
47
35
  datum = "0" if datum == false
48
36
 
37
+ #p datum.to_s + EOL
49
38
  server[:socket].syswrite(datum.to_s + EOL)
50
39
  end
51
40
  end
52
41
 
53
42
  # At minimum, Outgoing message contains message_id and version.
54
43
  # Most messages also contain (ticker, request or order) :id.
44
+ # Then, content of @data Hash is encoded per instructions in data_map.
55
45
  def encode
56
46
  [self.class.message_id,
57
47
  self.class.version,
58
- @data[:id] || []]
48
+ @data[:id] || @data[:ticker_id] || @data[:request_id]|| @data[:order_id] || [],
49
+ self.class.data_map.map do |(field, default_method, args)|
50
+ case
51
+ when default_method.nil?
52
+ @data[field]
53
+
54
+ when default_method.is_a?(Symbol) # method name with args
55
+ @data[field].send default_method, *args
56
+
57
+ when default_method.respond_to?(:call) # callable with args
58
+ default_method.call @data[field], *args
59
+
60
+ else # default
61
+ @data[field].nil? ? default_method : @data[field] # may be false still
62
+ end
63
+ end
64
+ ].flatten
59
65
  end
60
66
  end # AbstractMessage
61
67
 
62
- # Macro that defines short message classes using a one-liner
63
- def self.def_message message_id, version=1, *keys
64
- Class.new(AbstractMessage) do
65
- @message_id = message_id
66
- @version = version
67
-
68
- define_method :encode do
69
- [super(), keys.map { |key| @data[key] }]
70
- end unless keys.empty?
71
- end
72
- end
73
-
74
68
  ### Defining (short) Outgoing Message classes for IB:
75
69
 
76
70
  ## Empty messages (no data)
@@ -104,35 +98,151 @@ module IB
104
98
 
105
99
  ## Data format is: @data = { :id => request_id }
106
100
  CancelFundamentalData = def_message 53
107
- CancelImpliedVolatility = def_message 56
108
- CancelCalculateImpliedVolatility = CancelImpliedVolatility
109
- CancelOptionPrice = def_message 57
110
- CancelCalculateOptionPrice = CancelOptionPrice
101
+ CancelCalculateImpliedVolatility = CancelImpliedVolatility = def_message(56)
102
+ CancelCalculateOptionPrice = CancelOptionPrice = def_message(57)
111
103
 
112
- ## Data format is: @data ={ :id => order-id-to-cancel }
104
+ ## Data format is: @data ={ :id => order_id to cancel }
113
105
  CancelOrder = def_message 4
114
106
 
115
- ## These messages contain just one or two keys, shown in the end of definition
116
- # @data = { :number_of_ids => int }
117
- RequestIds = def_message 8, 1, :number_of_ids
107
+ ## These messages contain just one or two extra fields:
108
+
109
+ # Request the next valid ID that can be used when placing an order. Responds with
110
+ # NextValidId message, and the id returned is that next valid Id for orders.
111
+ # That ID will reflect any autobinding that has occurred (which generates new
112
+ # IDs and increments the next valid ID therein).
113
+ # @data = { :number of ids requested => int } NB: :number option is ignored by TWS!
114
+ RequestIds = def_message 8, [:number, 1]
118
115
  # data = { :all_messages => boolean }
119
- RequestNewsBulletins = def_message 12, 1, :all_messages
116
+ RequestNewsBulletins = def_message 12, :all_messages
120
117
  # data = { :log_level => int }
121
- SetServerLoglevel = def_message 14, 1, :log_level
118
+ SetServerLoglevel = def_message 14, :log_level
122
119
  # data = { :auto_bind => boolean }
123
- RequestAutoOpenOrders = def_message 15, 1, :auto_bind
120
+ RequestAutoOpenOrders = def_message 15, :auto_bind
124
121
  # data = { :fa_data_type => int }
125
- RequestFA = def_message 18, 1, :fa_data_type
122
+ RequestFA = def_message 18, :fa_data_type
126
123
  # data = { :fa_data_type => int, :xml => String }
127
- ReplaceFA = def_message 19, 1, :fa_data_type, :xml
124
+ ReplaceFA = def_message 19, :fa_data_type, :xml
125
+
128
126
  # @data = { :subscribe => boolean,
129
127
  # :account_code => Advisor accounts only. Empty ('') for a standard account. }
130
- RequestAccountData = def_message 6, 2, :subscribe, :account_code
131
- RequestAccountUpdates = RequestAccountData
128
+ RequestAccountUpdates = RequestAccountData = def_message([6, 2],
129
+ [:subscribe, true],
130
+ :account_code)
132
131
 
132
+ # data => { :id => request_id (int), :contract => Contract }
133
+ RequestContractDetails = RequestContractData =
134
+ def_message([9, 6],
135
+ [:contract, :serialize_short, [:con_id, :include_expired, :sec_id]])
136
+
137
+ # data = { :id => ticker_id (int), :contract => Contract, :num_rows => int }
138
+ RequestMarketDepth = def_message([10, 3],
139
+ [:contract, :serialize_short, []],
140
+ :num_rows)
133
141
 
134
142
  ### Defining (complex) Outgoing Message classes for IB:
135
143
 
144
+ # When this message is sent, TWS responds with ExecutionData messages, each
145
+ # containing the execution report that meets the specified criteria.
146
+ # @data={:id => int: :request_id,
147
+ # :client_id => int: Filter the results based on the clientId.
148
+ # :acct_code => Filter the results based on based on account code.
149
+ # Note: this is only relevant for Financial Advisor accts.
150
+ # :sec_type => Filter the results based on the order security type.
151
+ # :time => Filter the results based on execution reports received
152
+ # after the specified time - format "yyyymmdd-hh:mm:ss"
153
+ # :symbol => Filter the results based on the order symbol.
154
+ # :exchange => Filter the results based on the order exchange
155
+ # :side => Filter the results based on the order action: BUY/SELL/SSHORT
156
+ RequestExecutions = def_message([7, 3],
157
+ :client_id,
158
+ :acct_code,
159
+ :time, # Format "yyyymmdd-hh:mm:ss"
160
+ :symbol,
161
+ :sec_type,
162
+ :exchange,
163
+ :side)
164
+
165
+ # data = { :id => ticker_id (int),
166
+ # :contract => Contract,
167
+ # :exercise_action => int, 1 = exercise, 2 = lapse
168
+ # :exercise_quantity => int, The number of contracts to be exercised
169
+ # :account => string,
170
+ # :override => int: Specifies whether your setting will override the
171
+ # system's natural action. For example, if your action
172
+ # is "exercise" and the option is not in-the-money, by
173
+ # natural action the option would not exercise. If you
174
+ # have override set to "yes" the natural action would be
175
+ # overridden and the out-of-the money option would be
176
+ # exercised. Values are:
177
+ # � 0 = do not override
178
+ # � 1 = override
179
+ ExerciseOptions = def_message(21,
180
+ [:contract, :serialize_short],
181
+ :exercise_action,
182
+ :exercise_quantity,
183
+ :account,
184
+ :override)
185
+
186
+ # @data={:id => int: ticker_id - Must be a unique value. When the market data
187
+ # returns, it will be identified by this tag,
188
+ # :contract => Models::Contract, requested contract.
189
+ # :tick_list => String: comma delimited list of requested tick groups:
190
+ # Group ID - Description - Requested Tick Types
191
+ # 100 - Option Volume (currently for stocks) - 29, 30
192
+ # 101 - Option Open Interest (currently for stocks) - 27, 28
193
+ # 104 - Historical Volatility (currently for stocks) - 23
194
+ # 106 - Option Implied Volatility (currently for stocks) - 24
195
+ # 162 - Index Future Premium - 31
196
+ # 165 - Miscellaneous Stats - 15, 16, 17, 18, 19, 20, 21
197
+ # 221 - Mark Price (used in TWS P&L computations) - 37
198
+ # 225 - Auction values (volume, price and imbalance) - 34, 35, 36
199
+ # 233 - RTVolume - 48
200
+ # 236 - Shortable - 46
201
+ # 256 - Inventory - ?
202
+ # 258 - Fundamental Ratios - 47
203
+ # 411 - Realtime Historical Volatility - 58
204
+ # :snapshot => bool: Check to return a single snapshot of market data and
205
+ # have the market data subscription canceled. Do not enter any
206
+ # :tick_list values if you use snapshot. }
207
+ RequestMarketData =
208
+ def_message([1, 9],
209
+ [:contract, :serialize_long, [:con_id]],
210
+ [:contract, :serialize_legs, []],
211
+ [:contract, :serialize_under_comp, []],
212
+ [:tick_list, lambda do |tick_list|
213
+ tick_list.is_a?(Array) ? tick_list.join(',') : (tick_list || '')
214
+ end, []],
215
+ [:snapshot, false])
216
+
217
+ # Send this message to receive Reuters global fundamental data. There must be
218
+ # a subscription to Reuters Fundamental set up in Account Management before
219
+ # you can receive this data.
220
+ # data = { :id => int: :request_id,
221
+ # :contract => Contract,
222
+ # :report_type => String: one of the following:
223
+ # 'Estimates', 'Financial Statements', 'Summary' }
224
+ RequestFundamentalData =
225
+ def_message(52,
226
+ [:contract, :serialize, [:primary_exchange]],
227
+ :report_type)
228
+
229
+ # data = { :request_id => int, :contract => Contract,
230
+ # :option_price => double, :under_price => double }
231
+ RequestCalculateImpliedVolatility = CalculateImpliedVolatility =
232
+ RequestImpliedVolatility =
233
+ def_message(54,
234
+ [:contract, :serialize_long, [:con_id]],
235
+ :option_price,
236
+ :under_price)
237
+
238
+ # data = { :request_id => int, :contract => Contract,
239
+ # :volatility => double, :under_price => double }
240
+ RequestCalculateOptionPrice = CalculateOptionPrice = RequestOptionPrice =
241
+ def_message(55,
242
+ [:contract, :serialize_long, [:con_id]],
243
+ :volatility,
244
+ :under_price)
245
+
136
246
  # Start receiving market scanner results through the ScannerData messages.
137
247
  # @data = { :id => ticker_id (int),
138
248
  # :number_of_rows => int: number of rows of data to return for a query.
@@ -176,79 +286,97 @@ module IB
176
286
  # To learn all valid parameter values that a scanner subscription can have,
177
287
  # first subscribe to ScannerParameters and send RequestScannerParameters message.
178
288
  # Available scanner parameters values will be listed in received XML document.
179
- class RequestScannerSubscription < AbstractMessage
180
- @message_id = 22
181
- @version = 3
289
+ RequestScannerSubscription =
290
+ def_message([22, 3],
291
+ [:number_of_rows, -1], # was: EOL,
292
+ :instrument,
293
+ :location_code,
294
+ :scan_code,
295
+ [:above_price, EOL],
296
+ [:below_price, EOL],
297
+ [:above_volume, EOL],
298
+ [:market_cap_above, EOL],
299
+ [:market_cap_below, EOL],
300
+ :moody_rating_above,
301
+ :moody_rating_below,
302
+ :sp_rating_above,
303
+ :sp_rating_below,
304
+ :maturity_date_above,
305
+ :maturity_date_below,
306
+ [:coupon_rate_above, EOL],
307
+ [:coupon_rate_below, EOL],
308
+ :exclude_convertible,
309
+ [:average_option_volume_above, EOL], # ?
310
+ :scanner_setting_pairs,
311
+ :stock_type_filter)
312
+
313
+ ### Even more complex Outgoing Message classes, overriding #encode method:
314
+
315
+ # Data format is { :id => order_id (int),
316
+ # :contract => Contract,
317
+ # :order => Order }
318
+ class PlaceOrder < AbstractMessage
319
+ @message_id = 3
320
+ @version = 31
182
321
 
183
322
  def encode
184
323
  [super,
185
- @data[:number_of_rows] || -1, # was: EOL,
186
- @data[:instrument],
187
- @data[:location_code],
188
- @data[:scan_code],
189
- @data[:above_price] || EOL,
190
- @data[:below_price] || EOL,
191
- @data[:above_volume] || EOL,
192
- @data[:market_cap_above] || EOL,
193
- @data[:market_cap_below] || EOL,
194
- @data[:moody_rating_above],
195
- @data[:moody_rating_below],
196
- @data[:sp_rating_above],
197
- @data[:sp_rating_below],
198
- @data[:maturity_date_above],
199
- @data[:maturity_date_below],
200
- @data[:coupon_rate_above] || EOL,
201
- @data[:coupon_rate_below] || EOL,
202
- @data[:exclude_convertible],
203
- @data[:average_option_volume_above] || EOL, # ?
204
- @data[:scanner_setting_pairs],
205
- @data[:stock_type_filter]
206
- ]
324
+ @data[:order].serialize_with(@data[:contract])].flatten
207
325
  end
208
- end # RequestScannerSubscription
326
+ end # PlaceOrder
209
327
 
210
- # @data={:id => int: ticker_id - Must be a unique value. When the market data
211
- # returns, it will be identified by this tag,
212
- # :contract => Models::Contract, requested contract.
213
- # :tick_list => String: comma delimited list of requested tick groups:
214
- # Group ID - Description - Requested Tick Types
215
- # 100 - Option Volume (currently for stocks) - 29, 30
216
- # 101 - Option Open Interest (currently for stocks) - 27, 28
217
- # 104 - Historical Volatility (currently for stocks) - 23
218
- # 106 - Option Implied Volatility (currently for stocks) - 24
219
- # 162 - Index Future Premium - 31
220
- # 165 - Miscellaneous Stats - 15, 16, 17, 18, 19, 20, 21
221
- # 221 - Mark Price (used in TWS P&L computations) - 37
222
- # 225 - Auction values (volume, price and imbalance) - 34, 35, 36
223
- # 233 - RTVolume - 48
224
- # 236 - Shortable - 46
225
- # 256 - Inventory - ?
226
- # 258 - Fundamental Ratios - 47
227
- # 411 - Realtime Historical Volatility - 58
228
- # :snapshot => bool: Check to return a single snapshot of market data and
229
- # have the market data subscription canceled. Do not enter any
230
- # :tick_list values if you use snapshot. }
231
- class RequestMarketData < AbstractMessage
232
- @message_id = 1
233
- @version = 9 # message version number
328
+ module DataParser
329
+ # Preprocessor for some data fields
330
+ def parse data
331
+ data_type = DATA_TYPES[data[:what_to_show]] || data[:what_to_show]
332
+ unless DATA_TYPES.values.include?(data_type)
333
+ raise ArgumentError.new(":what_to_show must be one of #{DATA_TYPES.inspect}.")
334
+ end
335
+
336
+ bar_size = BAR_SIZES[data[:bar_size]] || data[:bar_size]
337
+ unless BAR_SIZES.values.include?(bar_size)
338
+ raise ArgumentError.new(":bar_size must be one of #{BAR_SIZES.inspect}.")
339
+ end
340
+
341
+ contract = data[:contract].is_a?(Models::Contract) ?
342
+ data[:contract] : Models::Contract.from_ib_ruby(data[:contract])
343
+
344
+ [data_type, bar_size, contract]
345
+ end
346
+ end
347
+
348
+ # data = { :id => ticker_id (int),
349
+ # :contract => Contract ,
350
+ # :bar_size => int/Symbol? Currently only 5 second bars (2?) are supported,
351
+ # if any other value is used, an exception will be thrown.,
352
+ # :what_to_show => Symbol: Determines the nature of data being extracted.
353
+ # Valid values:
354
+ # :trades, :midpoint, :bid, :ask, :bid_ask,
355
+ # :historical_volatility, :option_implied_volatility,
356
+ # :option_volume, :option_open_interest
357
+ # - converts to "TRADES," "MIDPOINT," "BID," etc...
358
+ # :use_rth => int: 0 - all data available during the time span requested
359
+ # is returned, even data bars covering time intervals where the
360
+ # market in question was illiquid. 1 - only data within the
361
+ # "Regular Trading Hours" of the product in question is returned,
362
+ # even if the time span requested falls partially or completely
363
+ # outside of them.
364
+ class RequestRealTimeBars < AbstractMessage
365
+ @message_id = 50
366
+ @version = 1 # ?
367
+
368
+ include DataParser
234
369
 
235
370
  def encode
236
- tick_list = case @data[:tick_list]
237
- when nil
238
- ''
239
- when Array
240
- @data[:tick_list].join ','
241
- when String
242
- @data[:tick_list]
243
- end
371
+ data_type, bar_size, contract = parse @data
372
+
244
373
  [super,
245
- @data[:contract].serialize_long(:con_id),
246
- @data[:contract].serialize_legs,
247
- @data[:contract].serialize_under_comp,
248
- tick_list,
249
- @data[:snapshot] || false]
374
+ contract.serialize_long,
375
+ bar_size,
376
+ data_type.to_s.upcase,
377
+ @data[:use_rth]].flatten
250
378
  end
251
- end # RequestMarketData
379
+ end # RequestRealTimeBars
252
380
 
253
381
  # data = { :id => int: Ticker id, needs to be different than the reqMktData ticker
254
382
  # id. If you use the same ticker ID you used for the symbol when
@@ -325,19 +453,10 @@ module IB
325
453
  @message_id = 20
326
454
  @version = 4
327
455
 
328
- def encode
329
- data_type = DATA_TYPES[@data[:what_to_show]] || @data[:what_to_show]
330
- unless DATA_TYPES.values.include?(data_type)
331
- raise ArgumentError(":what_to_show must be one of #{DATA_TYPES}.")
332
- end
333
-
334
- bar_size = BAR_SIZES[@data[:bar_size]] || @data[:bar_size]
335
- unless BAR_SIZES.values.include?(bar_size)
336
- raise ArgumentError(":bar_size must be one of #{BAR_SIZES}.")
337
- end
456
+ include DataParser
338
457
 
339
- contract = @data[:contract].is_a?(Models::Contract) ?
340
- @data[:contract] : Models::Contract.from_ib_ruby(@data[:contract])
458
+ def encode
459
+ data_type, bar_size, contract = parse @data
341
460
 
342
461
  [super,
343
462
  contract.serialize_long(:include_expired),
@@ -347,193 +466,10 @@ module IB
347
466
  @data[:use_rth],
348
467
  data_type.to_s.upcase,
349
468
  @data[:format_date],
350
- contract.serialize_legs]
469
+ contract.serialize_legs].flatten
351
470
  end
352
471
  end # RequestHistoricalData
353
472
 
354
- # data = { :id => ticker_id (int),
355
- # :contract => Contract ,
356
- # :bar_size => int/Symbol? Currently only 5 second bars (2?) are supported,
357
- # if any other value is used, an exception will be thrown.,
358
- # :what_to_show => Symbol: Determines the nature of data being extracted.
359
- # Valid values:
360
- # :trades, :midpoint, :bid, :ask, :bid_ask,
361
- # :historical_volatility, :option_implied_volatility,
362
- # :option_volume, :option_open_interest
363
- # - converts to "TRADES," "MIDPOINT," "BID," etc...
364
- # :use_rth => int: 0 - all data available during the time span requested
365
- # is returned, even data bars covering time intervals where the
366
- # market in question was illiquid. 1 - only data within the
367
- # "Regular Trading Hours" of the product in question is returned,
368
- # even if the time span requested falls partially or completely
369
- # outside of them.
370
- class RequestRealTimeBars < AbstractMessage
371
- @message_id = 50
372
-
373
- def encode
374
- data_type = DATA_TYPES[@data[:what_to_show]] || @data[:what_to_show]
375
- unless DATA_TYPES.values.include?(data_type)
376
- raise ArgumentError(":what_to_show must be one of #{DATA_TYPES}.")
377
- end
378
-
379
- bar_size = BAR_SIZES[@data[:bar_size]] || @data[:bar_size]
380
- unless BAR_SIZES.values.include?(bar_size)
381
- raise ArgumentError(":bar_size must be one of #{BAR_SIZES}.")
382
- end
383
-
384
- contract = @data[:contract].is_a?(Models::Contract) ?
385
- @data[:contract] : Models::Contract.from_ib_ruby(@data[:contract])
386
-
387
- [super,
388
- contract.serialize_long,
389
- bar_size,
390
- data_type.to_s.upcase,
391
- @data[:use_rth]]
392
- end
393
- end # RequestRealTimeBars
394
-
395
- # data => { :id => request_id (int), :contract => Contract }
396
- class RequestContractData < AbstractMessage
397
- @message_id = 9
398
- @version = 6
399
-
400
- def encode
401
- [super,
402
- @data[:contract].serialize_short(:con_id, :include_expired, :sec_id)]
403
- end
404
- end # RequestContractData
405
- RequestContractDetails = RequestContractData # alias
406
-
407
- # data = { :id => ticker_id (int), :contract => Contract, :num_rows => int }
408
- class RequestMarketDepth < AbstractMessage
409
- @message_id = 10
410
- @version = 3
411
-
412
- def encode
413
- [super,
414
- @data[:contract].serialize_short,
415
- @data[:num_rows]]
416
- end
417
- end # RequestMarketDepth
418
-
419
- # data = { :id => ticker_id (int),
420
- # :contract => Contract,
421
- # :exercise_action => int, 1 = exercise, 2 = lapse
422
- # :exercise_quantity => int, The number of contracts to be exercised
423
- # :account => string,
424
- # :override => int: Specifies whether your setting will override the
425
- # system's natural action. For example, if your action
426
- # is "exercise" and the option is not in-the-money, by
427
- # natural action the option would not exercise. If you
428
- # have override set to "yes" the natural action would be
429
- # overridden and the out-of-the money option would be
430
- # exercised. Values are:
431
- # � 0 = do not override
432
- # � 1 = override
433
- # }
434
- class ExerciseOptions < AbstractMessage
435
- @message_id = 21
436
-
437
- def encode
438
- [super,
439
- @data[:contract].serialize_short,
440
- @data[:exercise_action],
441
- @data[:exercise_quantity],
442
- @data[:account],
443
- @data[:override]]
444
- end
445
- end # ExerciseOptions
446
-
447
- # Data format is { :id => order_id (int),
448
- # :contract => Contract,
449
- # :order => Order }
450
- class PlaceOrder < AbstractMessage
451
- @message_id = 3
452
- @version = 31
453
-
454
- def encode
455
- [super,
456
- @data[:order].serialize_with(@data[:contract])]
457
- end
458
- end # PlaceOrder
459
-
460
- # When this message is sent, TWS responds with ExecutionData messages, each
461
- # containing the execution report that meets the specified criteria.
462
- # @data={:id => int: :request_id,
463
- # :client_id => int: Filter the results based on the clientId.
464
- # :acct_code => Filter the results based on based on account code.
465
- # Note: this is only relevant for Financial Advisor accts.
466
- # :sec_type => Filter the results based on the order security type.
467
- # :time => Filter the results based on execution reports received
468
- # after the specified time - format "yyyymmdd-hh:mm:ss"
469
- # :symbol => Filter the results based on the order symbol.
470
- # :exchange => Filter the results based on the order exchange
471
- # :side => Filter the results based on the order action: BUY/SELL/SSHORT
472
- class RequestExecutions < AbstractMessage
473
- @message_id = 7
474
- @version = 3
475
-
476
- def encode
477
- [super,
478
- @data[:client_id],
479
- @data[:acct_code],
480
- @data[:time], # Valid format for time is "yyyymmdd-hh:mm:ss"
481
- @data[:symbol],
482
- @data[:sec_type],
483
- @data[:exchange],
484
- @data[:side]]
485
- end
486
- end # RequestExecutions
487
-
488
- # Send this message to receive Reuters global fundamental data. There must be
489
- # a subscription to Reuters Fundamental set up in Account Management before
490
- # you can receive this data.
491
- # data = { :id => int: :request_id,
492
- # :contract => Contract,
493
- # :report_type => String: one of the following:
494
- # 'Estimates', 'Financial Statements', 'Summary' }
495
- class RequestFundamentalData < AbstractMessage
496
- @message_id = 52
497
-
498
- def encode
499
- [super,
500
- @data[:request_id],
501
- @data[:contract].serialize(:primary_exchange), # Minimal serialization set
502
- @data[:report_type]]
503
- end
504
- end # RequestFundamentalData
505
-
506
- # data = { :request_id => int, :contract => Contract,
507
- # :option_price => double, :under_price => double }
508
- class RequestImpliedVolatility < AbstractMessage
509
- @message_id = 54
510
-
511
- def encode
512
- [super,
513
- @data[:request_id],
514
- @data[:contract].serialize_long(:con_id),
515
- @data[:option_price],
516
- @data[:under_price]]
517
- end
518
- end # RequestImpliedVolatility
519
- CalculateImpliedVolatility = RequestImpliedVolatility
520
- RequestCalculateImpliedVolatility = RequestImpliedVolatility
521
-
522
- # data = { :request_id => int, :contract => Contract,
523
- # :volatility => double, :under_price => double }
524
- class RequestOptionPrice < AbstractMessage
525
- @message_id = 55
526
-
527
- def encode
528
- [super,
529
- @data[:request_id],
530
- @data[:contract].serialize_long(:con_id),
531
- @data[:volatility],
532
- @data[:under_price]]
533
- end
534
- end # RequestOptionPrice
535
- CalculateOptionPrice = RequestOptionPrice
536
- RequestCalculateOptionPrice = RequestOptionPrice
537
473
 
538
474
  end # module Outgoing
539
475
  end # module Messages
@@ -5,7 +5,7 @@ module IB
5
5
  # This is a single data point delivered by HistoricData messages.
6
6
  # Instantiate with a Hash of attributes, to be auto-set via initialize in Model.
7
7
  class Bar < Model
8
- attr_accessor :date, # The date-time stamp of the start of the bar. The format is
8
+ attr_accessor :time, # The date-time stamp of the start of the bar. The format is
9
9
  # determined by the reqHistoricalData() formatDate parameter.
10
10
  :open, # The bar opening price.
11
11
  :high, # The high price during the time covered by the bar.
@@ -18,8 +18,8 @@ module IB
18
18
  # of trades that occurred during the time period the bar covers
19
19
 
20
20
  def to_s
21
- "<Bar #{@date}: wap: #{@wap}, OHLC: #{@open}, #{@high}, #{@low}, #{@close}, " +
22
- (@trades ? "trades: #{@trades}," : "") + " vol: #{@volume}, gaps? #{@has_gaps}>"
21
+ "<Bar #{time}: wap: #{wap}, OHLC: #{open}, #{high}, #{low}, #{close}, " +
22
+ (trades ? "trades: #{trades}," : "") + " vol: #{volume}, gaps? #{has_gaps}>"
23
23
  end
24
24
  end # class Bar
25
25
  end # module Models
@@ -23,11 +23,7 @@ module IB
23
23
  end
24
24
 
25
25
  def to_human
26
- "<Bag: " + [symbol, exchange, currency].join(" ") + " legs: " +
27
- (@legs_description ||
28
- @legs.map do |leg|
29
- "#{leg.action} #{leg.ratio} * #{leg.con_id}"
30
- end.join('|')) + ">"
26
+ "<Bag: #{[symbol, exchange, currency].join(' ')} legs: #{legs_description} >"
31
27
  end
32
28
 
33
29
  end # class Bag