ib-ruby 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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