ib-ruby 0.5.0 → 0.5.2

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.
@@ -83,7 +83,7 @@ module IB
83
83
  end
84
84
 
85
85
  # Macro that defines short message classes using a one-liner
86
- def self.def_message message_id, *keys, &human_block
86
+ def self.def_message message_id, *keys, &to_human
87
87
  base = keys.first.is_a?(Class) ? keys.shift : AbstractMessage
88
88
  Class.new(base) do
89
89
  @message_id = message_id
@@ -93,7 +93,7 @@ module IB
93
93
  load_map *keys
94
94
  end
95
95
 
96
- define_method(:to_human, &human_block) if human_block
96
+ define_method(:to_human, &to_human) if to_human
97
97
  end
98
98
  end
99
99
 
@@ -152,7 +152,7 @@ module IB
152
152
  NewsBulletins =
153
153
  def_message 14, [:id, :int], # unique incrementing bulletin ID.
154
154
  [:type, :int], # Type of bulletin. Valid values include:
155
- # 1 = Reqular news bulletin
155
+ # 1 = Regular news bulletin
156
156
  # 2 = Exchange no longer available for trading
157
157
  # 3 = Exchange is available for trading
158
158
  [:text, :string], # The bulletin's message text.
@@ -172,7 +172,7 @@ module IB
172
172
  # FA configuration information.
173
173
 
174
174
  # Receives an XML document that describes the valid parameters that a scanner
175
- # subscription can have.
175
+ # subscription can have (for outgoing RequestScannerSubscription message).
176
176
  ScannerParameters = def_message 19, [:xml, :string]
177
177
 
178
178
  # Receives the current system time on the server side.
@@ -287,10 +287,11 @@ module IB
287
287
  @message_id = 21
288
288
 
289
289
  # Read @data[key] if it was computed (received value above limit)
290
- # Leave @data[key] nil if received value below limit ("not yet computed" indicator)
290
+ # Leave @data[key] nil if received value below limit ("not yet computed")
291
291
  def read_computed key, limit
292
- value = @socket.read_decimal # limit-1 is the "not yet computed" indicator
293
- @data[key] = value < limit ? nil : value
292
+ value = @socket.read_decimal
293
+ # limit is the "not yet computed" indicator
294
+ @data[key] = value <= limit ? nil : value
294
295
  end
295
296
 
296
297
  def load
@@ -298,14 +299,14 @@ module IB
298
299
 
299
300
  @data[:id] = @socket.read_int # ticker_id
300
301
  @data[:tick_type] = @socket.read_int
301
- read_computed :implied_volatility, 0 #-1 is the "not yet computed" indicator
302
- read_computed :delta, -1 # -2 is the "not yet computed" indicator
303
- read_computed :option_price, 0 # -1 is the "not yet computed" indicator
304
- read_computed :pv_dividend, 0 # -1 is the "not yet computed" indicator
305
- read_computed :gamma, -1 # -2 is the "not yet computed" indicator
306
- read_computed :vega, -1 # -2 is the "not yet computed" indicator
307
- read_computed :theta, -1 # -2 is the "not yet computed" indicator
308
- read_computed :under_price, 0 # -1 is the "not yet computed" indicator
302
+ read_computed :implied_volatility, -1 #-1 is the "not yet computed" indicator
303
+ read_computed :delta, -2 # -2 is the "not yet computed" indicator
304
+ read_computed :option_price, -1 # -1 is the "not yet computed" indicator
305
+ read_computed :pv_dividend, -1 # -1 is the "not yet computed" indicator
306
+ read_computed :gamma, -2 # -2 is the "not yet computed" indicator
307
+ read_computed :vega, -2 # -2 is the "not yet computed" indicator
308
+ read_computed :theta, -2 # -2 is the "not yet computed" indicator
309
+ read_computed :under_price, -1 # -1 is the "not yet computed" indicator
309
310
  end
310
311
 
311
312
  def to_human
@@ -536,6 +537,7 @@ module IB
536
537
  :order_types => @socket.read_string,
537
538
  :valid_exchanges => @socket.read_string,
538
539
  :price_magnifier => @socket.read_int,
540
+
539
541
  :under_con_id => @socket.read_int,
540
542
  :long_name => @socket.read_string,
541
543
  :primary_exchange => @socket.read_string,
@@ -548,6 +550,7 @@ module IB
548
550
  :liquid_hours => @socket.read_string
549
551
  end
550
552
  end # ContractData
553
+ ContractDetails = ContractData
551
554
 
552
555
  class ExecutionData < AbstractMessage
553
556
  @message_id = 11
@@ -587,11 +590,24 @@ module IB
587
590
  end # ExecutionData
588
591
 
589
592
  # HistoricalData contains following @data:
593
+ # General:
590
594
  # :id - The ID of the request to which this is responding
591
- # :count - Number of data points returned (size of :results).
595
+ # :count - Number of Historical data points returned (size of :results).
592
596
  # :results - an Array of Historical Data Bars
593
- # :start_date
594
- # :end_date
597
+ # :start_date - beginning of returned Historical data period
598
+ # :end_date - end of returned Historical data period
599
+ # Each returned Bar in @data[:results] Array contains this data:
600
+ # :date - The date-time stamp of the start of the bar. The format is
601
+ # determined by the RequestHistoricalData formatDate parameter.
602
+ # :open - The bar opening price.
603
+ # :high - The high price during the time covered by the bar.
604
+ # :low - The low price during the time covered by the bar.
605
+ # :close - The bar closing price.
606
+ # :volume - The volume during the time covered by the bar.
607
+ # :trades - When TRADES historical data is returned, represents number of trades
608
+ # that occurred during the time period the bar covers
609
+ # :wap - The weighted average price during the time covered by the bar.
610
+ # :has_gaps - Whether or not there are gaps in the data.
595
611
  class HistoricalData < AbstractMessage
596
612
  @message_id = 17
597
613
 
@@ -78,16 +78,18 @@ module IB
78
78
  CancelNewsBulletins = def_message 13
79
79
  RequestAllOpenOrders = def_message 16
80
80
  RequestManagedAccounts = def_message 17
81
+ # Requests an XML document that describes the valid parameters that a scanner
82
+ # subscription can have (for outgoing RequestScannerSubscription message).
81
83
  RequestScannerParameters = def_message 24
82
84
  RequestCurrentTime = def_message 49
83
85
  RequestGlobalCancel = def_message 58
84
86
 
85
87
  # Data format is: @data = { :id => ticker_id}
86
- CancelScannerSubscription = def_message 23
87
88
  CancelMarketData = def_message 2
89
+ CancelMarketDepth = def_message 11
90
+ CancelScannerSubscription = def_message 23
88
91
  CancelHistoricalData = def_message 25
89
92
  CancelRealTimeBars = def_message 51
90
- CancelMarketDepth = def_message 11
91
93
 
92
94
  # Data format is: @data = { :id => request_id }
93
95
  CancelFundamentalData = def_message 53
@@ -112,53 +114,84 @@ module IB
112
114
  RequestFA = def_message 18, 1, :fa_data_type
113
115
  # data = { :fa_data_type => int, :xml => String }
114
116
  ReplaceFA = def_message 19, 1, :fa_data_type, :xml
115
-
116
- # Data is { :subscribe => boolean,
117
- # :account_code => String: only necessary for advisor accounts. Set it
118
- # to empty ('') for a standard account. }
119
- class RequestAccountData < AbstractMessage
120
- @message_id = 6
121
- @version = 2
122
-
123
- def encode
124
- [super,
125
- @data[:subscribe],
126
- @data[:account_code] || '']
127
- end
128
- end
117
+ # @data = { :subscribe => boolean,
118
+ # :account_code => Advisor accounts only. Empty ('') for a standard account. }
119
+ RequestAccountData = def_message 6, 2, :subscribe, :account_code
129
120
  RequestAccountUpdates = RequestAccountData
130
121
 
131
122
 
132
123
  ### Defining (complex) Outgoing Message classes for IB:
133
124
 
134
- # data = { :id => ticker_id (int), :subscription => ScannerSubscription}
125
+ # Start receiving market scanner results through the ScannerData messages.
126
+ # @data = { :id => ticker_id (int),
127
+ # :number_of_rows => int: number of rows of data to return for a query.
128
+ # :instrument => The instrument type for the scan. Values include
129
+ # 'STK', - US stocks
130
+ # 'STOCK.HK' - Asian stocks
131
+ # 'STOCK.EU' - European stocks
132
+ # :location_code => Legal Values include:
133
+ # � STK.US - US stocks
134
+ # � STK.US.MAJOR - US stocks (without pink sheet)
135
+ # � STK.US.MINOR - US stocks (only pink sheet)
136
+ # � STK.HK.SEHK - Hong Kong stocks
137
+ # � STK.HK.ASX - Australian Stocks
138
+ # � STK.EU - European stocks
139
+ # :scan_code => The type of the scan, such as HIGH_OPT_VOLUME_PUT_CALL_RATIO.
140
+ # :above_price => double: Only contracts with a price above this value.
141
+ # :below_price => double: Only contracts with a price below this value.
142
+ # :above_volume => int: Only contracts with a volume above this value.
143
+ # :market_cap_above => double: Only contracts with a market cap above this
144
+ # :market_cap_below => double: Only contracts with a market cap below this value.
145
+ # :moody_rating_above => Only contracts with a Moody rating above this value.
146
+ # :moody_rating_below => Only contracts with a Moody rating below this value.
147
+ # :sp_rating_above => Only contracts with an S&P rating above this value.
148
+ # :sp_rating_below => Only contracts with an S&P rating below this value.
149
+ # :maturity_date_above => Only contracts with a maturity date later than this
150
+ # :maturity_date_below => Only contracts with a maturity date earlier than this
151
+ # :coupon_rate_above => double: Only contracts with a coupon rate above this
152
+ # :coupon_rate_below => double: Only contracts with a coupon rate below this
153
+ # :exclude_convertible => Exclude convertible bonds.
154
+ # :scanner_setting_pairs => Used with the scan_code to help further narrow your query.
155
+ # Scanner Setting Pairs are delimited by slashes, making
156
+ # this parameter open ended. Example is "Annual,true" -
157
+ # when used with 'Top Option Implied Vol % Gainers' scan
158
+ # would return annualized volatilities.
159
+ # :average_option_volume_above => int: Only contracts with average volume above this
160
+ # :stock_type_filter => Valid values are:
161
+ # 'ALL' (excludes nothing)
162
+ # 'STOCK' (excludes ETFs)
163
+ # 'ETF' (includes ETFs) }
164
+ # ------------
165
+ # To learn all valid parameter values that a scanner subscription can have,
166
+ # first subscribe to ScannerParameters and send RequestScannerParameters message.
167
+ # Available scanner parameters values will be listed in received XML document.
135
168
  class RequestScannerSubscription < AbstractMessage
136
169
  @message_id = 22
137
170
  @version = 3
138
171
 
139
172
  def encode
140
173
  [super,
141
- @data[:subscription].number_of_rows || EOL,
142
- @data[:subscription].instrument,
143
- @data[:subscription].location_code,
144
- @data[:subscription].scan_code,
145
- @data[:subscription].above_price || EOL,
146
- @data[:subscription].below_price || EOL,
147
- @data[:subscription].above_volume || EOL,
148
- @data[:subscription].market_cap_above || EOL,
149
- @data[:subscription].market_cap_below || EOL,
150
- @data[:subscription].moody_rating_above,
151
- @data[:subscription].moody_rating_below,
152
- @data[:subscription].sp_rating_above,
153
- @data[:subscription].sp_rating_below,
154
- @data[:subscription].maturity_date_above,
155
- @data[:subscription].maturity_date_below,
156
- @data[:subscription].coupon_rate_above || EOL,
157
- @data[:subscription].coupon_rate_below || EOL,
158
- @data[:subscription].exclude_convertible,
159
- @data[:subscription].average_option_volume_above,
160
- @data[:subscription].scanner_setting_pairs,
161
- @data[:subscription].stock_type_filter
174
+ @data[:number_of_rows] || -1, # was: EOL,
175
+ @data[:instrument],
176
+ @data[:location_code],
177
+ @data[:scan_code],
178
+ @data[:above_price] || EOL,
179
+ @data[:below_price] || EOL,
180
+ @data[:above_volume] || EOL,
181
+ @data[:market_cap_above] || EOL,
182
+ @data[:market_cap_below] || EOL,
183
+ @data[:moody_rating_above],
184
+ @data[:moody_rating_below],
185
+ @data[:sp_rating_above],
186
+ @data[:sp_rating_below],
187
+ @data[:maturity_date_above],
188
+ @data[:maturity_date_below],
189
+ @data[:coupon_rate_above] || EOL,
190
+ @data[:coupon_rate_below] || EOL,
191
+ @data[:exclude_convertible],
192
+ @data[:average_option_volume_above] || EOL, # ?
193
+ @data[:scanner_setting_pairs],
194
+ @data[:stock_type_filter]
162
195
  ]
163
196
  end
164
197
  end # RequestScannerSubscription
@@ -198,8 +231,7 @@ module IB
198
231
  @data[:tick_list]
199
232
  end
200
233
  [super,
201
- @data[:contract].con_id, # part of serialize?
202
- @data[:contract].serialize,
234
+ @data[:contract].serialize_long(:con_id),
203
235
  @data[:contract].serialize_combo_legs,
204
236
  @data[:contract].serialize_under_comp,
205
237
  tick_list,
@@ -290,8 +322,7 @@ module IB
290
322
  @data[:contract] : Models::Contract.from_ib_ruby(@data[:contract])
291
323
 
292
324
  [super,
293
- contract.serialize,
294
- contract.include_expired,
325
+ contract.serialize_long(:include_expired),
295
326
  @data[:end_date_time],
296
327
  @data[:bar_size],
297
328
  @data[:duration],
@@ -329,7 +360,7 @@ module IB
329
360
  @data[:contract] : Models::Contract.from_ib_ruby(@data[:contract])
330
361
 
331
362
  [super,
332
- contract.serialize,
363
+ contract.serialize_long,
333
364
  @data[:bar_size],
334
365
  @data[:what_to_show].to_s.upcase,
335
366
  @data[:use_rth]]
@@ -343,11 +374,7 @@ module IB
343
374
 
344
375
  def encode
345
376
  [super,
346
- @data[:contract].con_id, # part of serialize?
347
- @data[:contract].serialize(:short),
348
- @data[:contract].include_expired,
349
- @data[:contract].sec_id_type,
350
- @data[:contract].sec_id]
377
+ @data[:contract].serialize_short(:con_id, :include_expired, :sec_id)]
351
378
  end
352
379
  end # RequestContractData
353
380
  RequestContractDetails = RequestContractData # alias
@@ -359,7 +386,7 @@ module IB
359
386
 
360
387
  def encode
361
388
  [super,
362
- @data[:contract].serialize(:short),
389
+ @data[:contract].serialize_short,
363
390
  @data[:num_rows]]
364
391
  end
365
392
  end # RequestMarketDepth
@@ -384,7 +411,7 @@ module IB
384
411
 
385
412
  def encode
386
413
  [super,
387
- @data[:contract].serialize(:short),
414
+ @data[:contract].serialize_short,
388
415
  @data[:exercise_action],
389
416
  @data[:exercise_quantity],
390
417
  @data[:account],
@@ -398,76 +425,10 @@ module IB
398
425
  class PlaceOrder < AbstractMessage
399
426
  @message_id = 3
400
427
  @version = 31
401
- # int VERSION = (m_serverVersion < MIN_SERVER_VER_NOT_HELD) ? 27 : 31;
402
428
 
403
429
  def encode
404
430
  [super,
405
- @data[:contract].serialize,
406
- @data[:contract].sec_id_type, # Unimplemented?
407
- @data[:contract].sec_id, # Unimplemented?
408
- @data[:order].action, # send main order fields
409
- @data[:order].total_quantity,
410
- @data[:order].order_type,
411
- @data[:order].limit_price,
412
- @data[:order].aux_price,
413
- @data[:order].tif, # send extended order fields
414
- @data[:order].oca_group,
415
- @data[:order].account,
416
- @data[:order].open_close,
417
- @data[:order].origin,
418
- @data[:order].order_ref,
419
- @data[:order].transmit,
420
- @data[:order].parent_id,
421
- @data[:order].block_order,
422
- @data[:order].sweep_to_fill,
423
- @data[:order].display_size,
424
- @data[:order].trigger_method,
425
- @data[:order].outside_rth, # was: ignore_rth
426
- @data[:order].hidden,
427
- @data[:contract].serialize_combo_legs(:long),
428
- '', # send deprecated shares_allocation field
429
- @data[:order].discretionary_amount,
430
- @data[:order].good_after_time,
431
- @data[:order].good_till_date,
432
- @data[:order].fa_group,
433
- @data[:order].fa_method,
434
- @data[:order].fa_percentage,
435
- @data[:order].fa_profile,
436
- # Institutional short sale slot fields:
437
- @data[:order].short_sale_slot, # 0 only for retail, 1 or 2 for institution
438
- @data[:order].designated_location, # only populate when short_sale_slot == 2
439
- @data[:order].oca_type,
440
- @data[:order].rule_80a,
441
- @data[:order].settling_firm,
442
- @data[:order].all_or_none,
443
- @data[:order].min_quantity || EOL,
444
- @data[:order].percent_offset || EOL,
445
- @data[:order].etrade_only,
446
- @data[:order].firm_quote_only,
447
- @data[:order].nbbo_price_cap || EOL,
448
- @data[:order].auction_strategy || EOL,
449
- @data[:order].starting_price || EOL,
450
- @data[:order].stock_ref_price || EOL,
451
- @data[:order].delta || EOL,
452
- @data[:order].stock_range_lower || EOL,
453
- @data[:order].stock_range_upper || EOL,
454
- @data[:order].override_percentage_constraints,
455
- @data[:order].volatility || EOL, # Volatility orders
456
- @data[:order].volatility_type || EOL, # Volatility orders
457
- @data[:order].delta_neutral_order_type, # Volatility orders
458
- @data[:order].delta_neutral_aux_price || EOL, # Volatility orders
459
- @data[:order].continuous_update, # Volatility orders
460
- @data[:order].reference_price_type || EOL, # Volatility orders
461
- @data[:order].trail_stop_price || EOL, # TRAIL_STOP_LIMIT stop price
462
- @data[:order].scale_init_level_size || EOL, # Scale Orders
463
- @data[:order].scale_subs_level_size || EOL, # Scale Orders
464
- @data[:order].scale_price_increment || EOL, # Scale Orders
465
- @data[:order].clearing_account,
466
- @data[:order].clearing_intent,
467
- @data[:order].not_held,
468
- @data[:contract].serialize_under_comp,
469
- @data[:order].serialize_algo,
470
- @data[:order].what_if]
431
+ @data[:order].serialize_with(@data[:contract])]
471
432
  end
472
433
  end # PlaceOrder
473
434
 
@@ -488,7 +449,6 @@ module IB
488
449
  @version = 3
489
450
 
490
451
  def encode
491
-
492
452
  [super,
493
453
  @data[:client_id],
494
454
  @data[:acct_code],
@@ -497,7 +457,7 @@ module IB
497
457
  @data[:sec_type],
498
458
  @data[:exchange],
499
459
  @data[:side]]
500
- end # encode
460
+ end
501
461
  end # RequestExecutions
502
462
 
503
463
  # Send this message to receive Reuters global fundamental data. There must be
@@ -506,22 +466,14 @@ module IB
506
466
  # data = { :id => int: :request_id,
507
467
  # :contract => Contract,
508
468
  # :report_type => String: one of the following:
509
- # Estimates
510
- # Financial Statements
511
- # Summary
512
- # }
469
+ # 'Estimates', 'Financial Statements', 'Summary' }
513
470
  class RequestFundamentalData < AbstractMessage
514
471
  @message_id = 52
515
472
 
516
473
  def encode
517
474
  [super,
518
475
  @data[:request_id],
519
- @data[:contract].symbol, # Yet another Contract serialization!
520
- @data[:contract].sec_type,
521
- @data[:contract].exchange,
522
- @data[:contract].primary_exchange,
523
- @data[:contract].currency,
524
- @data[:contract].local_symbol,
476
+ @data[:contract].serialize(:primary_exchange), # Minimal serialization set
525
477
  @data[:report_type]]
526
478
  end
527
479
  end # RequestFundamentalData
@@ -534,8 +486,7 @@ module IB
534
486
  def encode
535
487
  [super,
536
488
  @data[:request_id],
537
- @data[:contract].con_id, # part of serialize?
538
- @data[:contract].serialize,
489
+ @data[:contract].serialize_long(:con_id),
539
490
  @data[:option_price],
540
491
  @data[:under_price]]
541
492
  end
@@ -551,8 +502,7 @@ module IB
551
502
  def encode
552
503
  [super,
553
504
  @data[:request_id],
554
- @data[:contract].con_id, # part of serialize?
555
- @data[:contract].serialize,
505
+ @data[:contract].serialize_long(:con_id),
556
506
  @data[:volatility],
557
507
  @data[:under_price]]
558
508
  end
@@ -165,30 +165,29 @@ module IB
165
165
  @strike = 0
166
166
  end
167
167
 
168
- # This returns an Array of data from the given contract, in standard format.
168
+ # This returns an Array of data from the given contract.
169
169
  # Different messages serialize contracts differently. Go figure.
170
- # Note that it does not include the combo legs.
171
- def serialize(type = :long)
172
- [symbol,
170
+ # Note that it does NOT include the combo legs.
171
+ def serialize(*fields)
172
+ [(fields.include?(:con_id) ? [con_id] : []),
173
+ symbol,
173
174
  sec_type,
174
- expiry,
175
- strike,
176
- right,
177
- multiplier,
178
- exchange] +
179
- (type == :long ? [primary_exchange] : []) +
180
- [currency,
181
- local_symbol]
175
+ (fields.include?(:option) ? [expiry, strike, right, multiplier] : []),
176
+ exchange,
177
+ (fields.include?(:primary_exchange) ? [primary_exchange] : []),
178
+ currency,
179
+ local_symbol,
180
+ (fields.include?(:sec_id) ? [sec_id_type, sec_id] : []),
181
+ (fields.include?(:include_expired) ? [include_expired] : []),
182
+ ].flatten
182
183
  end
183
184
 
184
- # @Legacy
185
- def serialize_long(version)
186
- serialize(:long)
185
+ def serialize_long(*fields)
186
+ serialize(:option, :primary_exchange, *fields)
187
187
  end
188
188
 
189
- # @Legacy
190
- def serialize_short(version)
191
- serialize(:short)
189
+ def serialize_short(*fields)
190
+ serialize(:option, *fields)
192
191
  end
193
192
 
194
193
  # This produces a string uniquely identifying this contract, in the format used