ib-ruby 0.5.19 → 0.5.21

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/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