ib-ruby 0.4.3 → 0.4.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +32 -0
  2. data/HISTORY +68 -0
  3. data/README.rdoc +9 -6
  4. data/VERSION +1 -1
  5. data/bin/account_info +29 -0
  6. data/bin/contract_details +37 -0
  7. data/bin/depth_of_market +43 -0
  8. data/bin/historic_data +62 -0
  9. data/bin/{RequestHistoricData → historic_data_cli} +46 -91
  10. data/bin/market_data +49 -0
  11. data/bin/option_data +45 -0
  12. data/bin/template +21 -0
  13. data/bin/time_and_sales +63 -0
  14. data/lib/ib-ruby/connection.rb +166 -0
  15. data/lib/ib-ruby/constants.rb +91 -0
  16. data/lib/ib-ruby/messages/incoming.rb +807 -0
  17. data/lib/ib-ruby/messages/outgoing.rb +573 -0
  18. data/lib/ib-ruby/messages.rb +8 -1445
  19. data/lib/ib-ruby/models/bar.rb +26 -0
  20. data/lib/ib-ruby/models/contract.rb +335 -0
  21. data/lib/ib-ruby/models/execution.rb +55 -0
  22. data/lib/ib-ruby/models/model.rb +20 -0
  23. data/lib/ib-ruby/models/order.rb +262 -0
  24. data/lib/ib-ruby/models.rb +11 -0
  25. data/lib/ib-ruby/socket.rb +50 -0
  26. data/lib/ib-ruby/symbols/forex.rb +32 -72
  27. data/lib/ib-ruby/symbols/futures.rb +47 -68
  28. data/lib/ib-ruby/symbols/options.rb +30 -0
  29. data/lib/ib-ruby/symbols/stocks.rb +23 -0
  30. data/lib/ib-ruby/symbols.rb +9 -0
  31. data/lib/ib-ruby.rb +7 -8
  32. data/lib/legacy/bin/account_info_old +36 -0
  33. data/lib/legacy/bin/historic_data_old +81 -0
  34. data/lib/legacy/bin/market_data_old +68 -0
  35. data/lib/legacy/datatypes.rb +485 -0
  36. data/lib/legacy/ib-ruby.rb +10 -0
  37. data/lib/legacy/ib.rb +226 -0
  38. data/lib/legacy/messages.rb +1458 -0
  39. data/lib/version.rb +2 -3
  40. data/spec/ib-ruby/models/contract_spec.rb +261 -0
  41. data/spec/ib-ruby/models/order_spec.rb +64 -0
  42. data/spec/ib-ruby_spec.rb +0 -131
  43. metadata +106 -76
  44. data/bin/AccountInfo +0 -67
  45. data/bin/HistoricToCSV +0 -111
  46. data/bin/RequestMarketData +0 -78
  47. data/bin/SimpleTimeAndSales +0 -98
  48. data/bin/ib-ruby +0 -8
  49. data/lib/ib-ruby/datatypes.rb +0 -400
  50. data/lib/ib-ruby/ib.rb +0 -242
@@ -1,1449 +1,12 @@
1
- #
2
- # Copyright (C) 2006 Blue Voodoo Magic LLC.
3
- #
4
- # This library is free software; you can redistribute it and/or modify
5
- # it under the terms of the GNU Lesser General Public License as
6
- # published by the Free Software Foundation; either version 2.1 of the
7
- # License, or (at your option) any later version.
8
- #
9
- # This library is distributed in the hope that it will be useful, but
10
- # WITHOUT ANY WARRANTY; without even the implied warranty of
11
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
- # Lesser General Public License for more details.
13
- #
14
- # You should have received a copy of the GNU Lesser General Public
15
- # License along with this library; if not, write to the Free Software
16
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
- # 02110-1301 USA
18
- #
19
- #
20
- # EClientSocket.java uses sendMax() rather than send() for a number of these.
21
- # It sends an EOL rather than a number if the value == Integer.MAX_VALUE (or Double.MAX_VALUE).
22
- # These fields are initialized to this MAX_VALUE.
23
- # This has been implemented with nils in Ruby to represent the case where an EOL should be sent.
24
-
25
1
  module IB
26
-
27
- #logger = Logger.new(STDERR)
28
-
29
- class ExtremelyAbstractMessage
30
- attr_reader :created_at
31
-
32
- def to_human
33
- self.inspect
34
- end
2
+ module Messages
35
3
  end
4
+ end
36
5
 
6
+ require 'ib-ruby/messages/outgoing'
7
+ require 'ib-ruby/messages/incoming'
37
8
 
38
- ################################################################
39
- #### Outgoing messages
40
- ################################################################
41
-
42
- module OutgoingMessages
43
- EOL = "\0"
44
-
45
- class AbstractMessage < ExtremelyAbstractMessage
46
- def self.message_id
47
- raise Exception("AbstractMessage.message_id called - you need to override this in a subclass.")
48
- end
49
-
50
- # data is a Hash.
51
- def initialize(data=nil)
52
- @created_at = Time.now
53
- @data = Datatypes::StringentHash.new(data)
54
- end # initialize
55
-
56
-
57
- # This causes the message to send itself over the server socket in server[:socket].
58
- # "server" is the @server instance variable from the IB object. You can also use this to e.g. get the server version number.
59
- #
60
- # Subclasses can either override this method for precise control
61
- # over how stuff gets sent to the server, or else define a
62
- # method queue() that returns an Array of elements that ought to
63
- # be sent to the server by calling to_s on each one and
64
- # postpending a '\0'.
65
- #
66
- def send(server)
67
- self.queue(server).each {|datum|
68
-
69
- # TWS wants to receive booleans as 1 or 0... rewrite as
70
- # necessary.
71
- datum = "1" if datum == true
72
- datum = "0" if datum == false
73
-
74
- server[:socket].syswrite(datum.to_s + "\0")
75
- }
76
- end
77
-
78
- def queue
79
- raise Exception("AbstractMessage.queue() called - you need to override this in a subclass.")
80
- end
81
-
82
-
83
- protected
84
-
85
- def requireVersion(server, version)
86
- raise(Exception.new("TWS version >= #{version} required.")) if server[:version] < version
87
- end
88
-
89
- # Returns EOL instead of datum if datum is nil, providing the same functionality as sendMax() in the Java version,
90
- # which uses Double.MAX_VALUE to mean "item not set" in a variable, and replaces that with EOL on send.
91
- def nilFilter(datum)
92
- datum.nil? ? EOL : datum
93
- end
94
-
95
- end # AbstractMessage
96
-
97
-
98
- # Data format is { :ticker_id => int, :contract => Datatypes::Contract }
99
- class RequestMarketData < AbstractMessage
100
- def self.message_id
101
- 1
102
- end
103
-
104
- def queue(server)
105
- queue = [ self.class.message_id,
106
- 5, # message version number
107
- @data[:ticker_id]
108
- ].concat(@data[:contract].serialize_long(server[:version]))
109
-
110
- queue.concat(@data[:contract].serialize_combo_legs
111
- ) if server[:version] >= 8 && @data[:contract].sec_type == "BAG" # I have no idea what "BAG" means. Copied from the Java code.
112
-
113
-
114
- queue
115
- end # queue
116
- end # RequestMarketData
117
-
118
- # Data format is { :ticker_id => int }
119
- class CancelMarketData < AbstractMessage
120
- def self.message_id
121
- 2
122
- end
123
- def queue(server)
124
- [ self.class.message_id,
125
- 1, # message version number
126
- @data[:ticker_id] ]
127
- end # queue
128
- end # CancelMarketData
129
-
130
- # Data format is { :order_id => int, :contract => Contract, :order => Order }
131
- class PlaceOrder < AbstractMessage
132
- def self.message_id
133
- 3
134
- end
135
-
136
- def queue(server)
137
- queue = [ self.class.message_id,
138
- 20, # version
139
- @data[:order_id],
140
- @data[:contract].symbol,
141
- @data[:contract].sec_type,
142
- @data[:contract].expiry,
143
- @data[:contract].strike,
144
- @data[:contract].right
145
- ]
146
- queue.push(@data[:contract].multiplier) if server[:version] >= 15
147
- queue.push(@data[:contract].exchange) if server[:version] >= 14
148
- queue.push(@data[:contract].currency)
149
- queue.push(@data[:contract].local_symbol) if server[:version] >= 2
150
-
151
- queue.concat([
152
- @data[:order].tif,
153
- @data[:order].oca_group,
154
- @data[:order].account,
155
- @data[:order].open_close,
156
- @data[:order].origin,
157
- @data[:order].order_ref,
158
- @data[:order].transmit
159
- ])
160
-
161
- queue.push(@data[:contract].parent_id) if server[:version] >= 4
162
-
163
- queue.concat([
164
- @data[:order].block_order,
165
- @data[:order].sweep_to_fill,
166
- @data[:order].display_size,
167
- @data[:order].trigger_method,
168
- @data[:order].ignore_rth
169
- ]) if server[:version] >= 5
170
-
171
- queue.push(@data[:order].hidden) if server[:version] >= 7
172
-
173
-
174
- queue.concat(@data[:contract].serialize_combo_legs(true)) if server[:version] >= 8 && @data[:contract].sec_type.upcase == "BAG" # "BAG" is defined as a constant in EClientSocket.java, line 45
175
-
176
- queue.push(@data[:order].shares_allocation) if server[:version] >= 9 # EClientSocket.java says this is deprecated. No idea.
177
- queue.push(@data[:order].discretionary_amount) if server[:version] >= 10
178
- queue.push(@data[:order].good_after_time) if server[:version] >= 11
179
- queue.push(@data[:order].good_till_date) if server[:version] >= 12
180
-
181
- queue.concat([
182
- @data[:order].fa_group,
183
- @data[:order].fa_method,
184
- @data[:order].fa_percentage,
185
- @data[:order].fa_profile
186
- ]) if server[:version] >= 13
187
-
188
- queue.concat([
189
- @data[:order].short_sale_slot,
190
- @data[:order].designated_location
191
- ]) if server[:version] >= 18
192
-
193
- queue.concat([
194
- @data[:order].oca_type,
195
- @data[:order].rth_only,
196
- @data[:order].rule_80a,
197
- @data[:order].settling_firm,
198
- @data[:order].all_or_none,
199
- nilFilter(@data[:order].min_quantity),
200
- nilFilter(@data[:order].percent_offset),
201
- @data[:order].etrade_only,
202
- @data[:order].firm_quote_only,
203
- nilFilter(@data[:order].nbbo_price_cap),
204
- nilFilter(@data[:order].auction_strategy),
205
- nilFilter(@data[:order].starting_price),
206
- nilFilter(@data[:order].stock_ref_price),
207
- nilFilter(@data[:order].delta),
208
-
209
- # Says the Java here:
210
- # "// Volatility orders had specific watermark price attribs in server version 26"
211
- # I have no idea what this means.
212
-
213
- ((server[:version] == 26 && @data[:order].order_type.upcase == "VOL") ? EOL : @data[:order].stock_range_lower),
214
- ((server[:version] == 26 && @data[:order].order_type.upcase == "VOL") ? EOL : @data[:order].stock_range_upper),
215
-
216
- ]) if server[:version] >= 19
217
-
218
- queue.push(@data[:order].override_percentage_constraints) if server[:version] >= 22
219
-
220
- # Volatility orders
221
- if server[:version] >= 26
222
- queue.concat([nilFilter(@data[:order].volatility),
223
- nilFilter(@data[:order].volatility_type) ])
224
-
225
- if server[:version] < 28
226
- queue.push(@data[:order].delta_neutral_order_type.upcase == "MKT")
227
- else
228
- queue.concat([@data[:order].delta_neutral_order_type,
229
- nilFilter(@data[:order].delta_neutral_aux_price)
230
- ])
231
- end
232
-
233
- queue.push(@data[:order].continuous_update)
234
- queue.concat([
235
- (@data[:order].order_type.upcase == "VOL" ? @data[:order].stock_range_lower : EOL),
236
- (@data[:order].order_type.upcase == "VOL" ? @data[:order].stock_range_upper : EOL)
237
- ]) if server[:version] == 26
238
-
239
- queue.push(@data[:order].reference_price_type)
240
-
241
- end # if version >= 26
242
-
243
- queue
244
- end # queue()
245
-
246
- end # PlaceOrder
247
-
248
- # Data format is { :id => id-to-cancel }
249
- class CancelOrder < AbstractMessage
250
- def self.message_id
251
- 4
252
- end
253
-
254
- def queue(server)
255
- [
256
- self.class.message_id,
257
- 1, # version
258
- @data[:id]
259
- ]
260
- end # queue
261
- end # CancelOrder
262
-
263
- class RequestOpenOrders < AbstractMessage
264
- def self.message_id
265
- 5
266
- end
267
- def queue(server)
268
- [ self.class.message_id,
269
- 1 # version
270
- ]
271
- end
272
- end # RequestOpenOrders
273
-
274
- # Data is { :subscribe => boolean, :account_code => string }
275
- #
276
- # :account_code is only necessary for advisor accounts. Set it to
277
- # empty ('') for a standard account.
278
- #
279
- class RequestAccountData < AbstractMessage
280
- def self.message_id
281
- 6
282
- end
283
- def queue(server)
284
- queue = [ self.class.message_id,
285
- 2, # version
286
- @data[:subscribe]
287
- ]
288
- queue.push(@data[:account_code]) if server[:version] >= 9
289
- queue
290
- end
291
- end # RequestAccountData
292
-
293
-
294
- # data = { :filter => ExecutionFilter ]
295
- class RequestExecutions < AbstractMessage
296
- def self.message_id
297
- 7
298
- end
299
- def queue(server)
300
- queue = [ self.class.message_id,
301
- 2 # version
302
- ]
303
-
304
- queue.concat([
305
- @data[:filter].client_id,
306
- @data[:filter].acct_code,
307
-
308
- # The Java says: 'Note that the valid format for m_time is "yyyymmdd-hh:mm:ss"'
309
- @data[:filter].time,
310
- @data[:filter].symbol,
311
- @data[:filter].sec_type,
312
- @data[:filter].exchange,
313
- @data[:filter].side
314
- ]) if server[:version] >= 9
315
-
316
- queue
317
- end # queue
318
- end # RequestExecutions
319
-
320
-
321
- # data = { :number_of_ids => int }
322
- class RequestIds < AbstractMessage
323
- def self.message_id
324
- 8
325
- end
326
-
327
- def queue(server)
328
- [ self.class.message_id,
329
- 1, # version
330
- @data[:number_of_ids]
331
- ]
332
- end
333
- end # RequestIds
334
-
335
-
336
- # data => { :contract => Contract }
337
- class RequestContractData < AbstractMessage
338
- def self.message_id
339
- 9
340
- end
341
-
342
- def queue(server)
343
- requireVersion(server, 4)
344
-
345
- queue = [
346
- self.class.message_id,
347
- 2, # version
348
- @data[:contract].symbol,
349
- @data[:contract].sec_type,
350
- @data[:contract].expiry,
351
- @data[:contract].strike,
352
- @data[:contract].right
353
- ]
354
- queue.push(@data[:contract].multiplier) if server[:version] >= 15
355
-
356
- queue.concat([
357
- @data[:contract].exchange,
358
- @data[:contract].currency,
359
- @data[:contract].local_symbol,
360
- ])
361
-
362
- queue
363
- end # send
364
- end # RequestContractData
365
-
366
- # data = { :ticker_id => int, :contract => Contract, :num_rows => int }
367
- class RequestMarketDepth < AbstractMessage
368
- def self.message_id
369
- 10
370
- end
371
-
372
- def queue(server)
373
- requireVersion(server, 6)
374
-
375
- queue = [ self.class.message_id,
376
- 3, # version
377
- @data[:ticker_id]
378
- ]
379
- queue.concat(@data[:contract].serialize_short(server[:version]))
380
- queue.push(@data[:num_rows]) if server[:version] >= 19
381
-
382
- queue
383
-
384
- end # queue
385
- end # RequestMarketDepth
386
-
387
- # data = { :ticker_id => int }
388
- class CancelMarketDepth < AbstractMessage
389
- def self.message_id
390
- 11
391
- end
392
- def queue(server)
393
- requireVersion(self, 6)
394
-
395
- [ self.class.message_id,
396
- 1, # version
397
- @data[:ticker_id]
398
- ]
399
- end
400
- end # CancelMarketDepth
401
-
402
-
403
- # data = { :all_messages => boolean }
404
- class RequestNewsBulletins < AbstractMessage
405
- def self.message_id
406
- 12
407
- end
408
-
409
- def queue(server)
410
- [ self.class.message_id,
411
- 1, # version
412
- @data[:all_messages]
413
- ]
414
- end
415
- end # RequestNewsBulletins
416
-
417
- class CancelNewsBulletins < AbstractMessage
418
- def self.message_id
419
- 13
420
- end
421
-
422
- def queue(server)
423
- [ self.class.message_id,
424
- 1 # version
425
- ]
426
- end
427
- end # CancelNewsBulletins
428
-
429
- # data = { :loglevel => int }
430
- class SetServerLoglevel < AbstractMessage
431
- def self.message_id
432
- 14
433
- end
434
-
435
- def queue(server)
436
- [ self.class.message_id,
437
- 1, # version
438
- @data[:loglevel]
439
- ]
440
- end
441
- end # SetServerLoglevel
442
-
443
- # data = { :auto_bind => boolean }
444
- class RequestAutoOpenOrders < AbstractMessage
445
- def self.message_id
446
- 15
447
- end
448
-
449
- def queue(server)
450
- [ self.class.message_id,
451
- 1, # version
452
- @data[:auto_bind]
453
- ]
454
- end
455
- end # RequestAutoOpenOrders
456
-
457
-
458
- class RequestAllOpenOrders < AbstractMessage
459
- def self.message_id
460
- 16
461
- end
462
-
463
- def queue(server)
464
- [ self.class.message_id,
465
- 1 # version
466
- ]
467
- end
468
- end # RequestAllOpenOrders
469
-
470
- class RequestManagedAccounts < AbstractMessage
471
- def self.message_id
472
- 17
473
- end
474
-
475
- def queue(server)
476
- [ self.class.message_id,
477
- 1 # version
478
- ]
479
- end
480
- end # RequestManagedAccounts
481
-
482
- # No idea what this is.
483
- # data = { :fa_data_type => int }
484
- class RequestFA < AbstractMessage
485
- def self.message_id
486
- 18
487
- end
488
-
489
- def queue(server)
490
- requireVersion(server, 13)
491
-
492
- [ self.class.message_id,
493
- 1, # version
494
- @data[:fa_data_type]
495
- ]
496
- end
497
- end # RequestFA
498
-
499
- # No idea what this is.
500
- # data = { :fa_data_type => int, :xml => string }
501
- class ReplaceFA < AbstractMessage
502
- def self.message_id
503
- 19
504
- end
505
-
506
- def queue(server)
507
- requireVersion(server, 13)
508
-
509
- [ self.class.message_id,
510
- 1, # version
511
- @data[:fa_data_type],
512
- @data[:xml]
513
- ]
514
- end
515
- end # ReplaceFA
516
-
517
- # data = { :ticker_id => int,
518
- # :contract => Contract,
519
- # :end_date_time => string,
520
- # :duration => string, # this specifies an integer number of seconds
521
- # :bar_size => int,
522
- # :what_to_show => symbol, # one of :trades, :midpoint, :bid, or :ask
523
- # :use_RTH => int,
524
- # :format_date => int
525
- # }
526
- #
527
- # Note that as of 4/07 there is no historical data available for forex spot.
528
- #
529
- # data[:contract] may either be a Contract object or a String. A String should be in serialize_ib_ruby format;
530
- # that is, it should be a colon-delimited string in the format:
531
- #
532
- # symbol:security_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
533
- #
534
- # Fields not needed for a particular security should be left blank (e.g. strike and right are only relevant for options.)
535
- #
536
- # For example, to query the British pound futures contract trading on Globex expiring in September, 2008,
537
- # the correct string is:
538
- #
539
- # GBP:FUT:200809:::62500:GLOBEX::USD:
540
- #
541
- # A Contract object will be automatically serialized into the required format.
542
- #
543
- # See also http://chuckcaplan.com/twsapi/index.php/void%20reqIntradayData%28%29
544
- # for general information about how TWS handles historic data requests, whence the following has been adapted:
545
- #
546
- # The server providing historical prices appears to not always be
547
- # available outside of market hours. If you call it outside of its
548
- # supported time period, or if there is otherwise a problem with
549
- # it, you will receive error #162 "Historical Market Data Service
550
- # query failed.:HMDS query returned no data."
551
- #
552
- # The "endDateTime" parameter accepts a string in the form
553
- # "yyyymmdd HH:mm:ss", with a time zone optionally allowed after a
554
- # space at the end of the string; e.g. "20050701 18:26:44 GMT"
555
- #
556
- # The ticker id needs to be different than the reqMktData ticker
557
- # id. If you use the same ticker ID you used for the symbol when
558
- # you did ReqMktData, nothing comes back for the historical data
559
- # call.
560
- #
561
- # Possible :bar_size values:
562
- # 1 = 1 sec
563
- # 2 = 5 sec
564
- # 3 = 15 sec
565
- # 4 = 30 sec
566
- # 5 = 1 minute
567
- # 6 = 2 minutes
568
- # 7 = 5 minutes
569
- # 8 = 15 minutes
570
- # 9 = 30 minutes
571
- # 10 = 1 hour
572
- # 11 = 1 day
573
- #
574
- # Values less than 4 do not appear to work for certain securities.
575
- #
576
- # The nature of the data extracted is governed by sending a string
577
- # having a value of "TRADES," "MIDPOINT," "BID," or "ASK." Here,
578
- # we require a symbol argument of :trades, :midpoint, :bid, or
579
- # :asked to be passed as data[:what_to_show].
580
- #
581
- # If data[:use_RTH] is set to 0, all data available during the time
582
- # span requested is returned, even data bars covering time
583
- # intervals where the market in question was illiquid. If useRTH
584
- # has a non-zero value, only data within the "Regular Trading
585
- # Hours" of the product in question is returned, even if the time
586
- # span requested falls partially or completely outside of them.
587
- #
588
- # Using a :format_date of 1 will cause the dates in the returned
589
- # messages with the historic data to be in a text format, like
590
- # "20050307 11:32:16". If you set :format_date to 2 instead, you
591
- # will get an offset in seconds from the beginning of 1970, which
592
- # is the same format as the UNIX epoch time.
593
- #
594
- # For backfill on futures data, you may need to leave the Primary
595
- # Exchange field of the Contract structure blank; see
596
- # http://www.interactivebrokers.com/discus/messages/2/28477.html?1114646754
597
- # [This message does not appear to exist anymore as of 4/07.]
598
-
599
- ALLOWED_HISTORICAL_TYPES = [:trades, :midpoint, :bid, :ask]
600
-
601
- class RequestHistoricalData < AbstractMessage
602
- # Enumeration of bar size types for convenience. These are passed to TWS as the (one-based!) index into the array.
603
- # Bar sizes less than 30 seconds do not work for some securities.
604
- BarSizes = [
605
- :invalid, # zero is not a valid barsize
606
- :second,
607
- :five_seconds,
608
- :fifteen_seconds,
609
- :thirty_seconds,
610
- :minute,
611
- :two_minutes,
612
- :five_minutes,
613
- :fifteen_minutes,
614
- :thirty_minutes,
615
- :hour,
616
- :day,
617
- ]
618
-
619
-
620
- def self.message_id
621
- 20
622
- end
623
-
624
- def queue(server)
625
- requireVersion(server, 16)
626
-
627
- if @data.has_key?(:what_to_show) && @data[:what_to_show].is_a?(String)
628
- @data[:what_to_show].downcase!
629
- @data[:what_to_show] = @data[:what_to_show].to_sym
630
- end
631
-
632
- raise ArgumentError("RequestHistoricalData: @data[:what_to_show] must be one of #{ALLOWED_HISTORICAL_TYPES.inspect}.") unless ALLOWED_HISTORICAL_TYPES.include?(@data[:what_to_show])
633
-
634
- queue = [ self.class.message_id,
635
- 3, # version
636
- @data[:ticker_id]
637
- ]
638
-
639
- contract = @data[:contract].is_a?(Datatypes::Contract) ? @data[:contract] : Datatypes::Contract.from_ib_ruby(@data[:contract])
640
- queue.concat(contract.serialize_long(server[:version]))
641
-
642
- queue.concat([
643
- @data[:end_date_time],
644
- @data[:bar_size]
645
- ]) if server[:version] > 20
646
-
647
-
648
- queue.concat([
649
- @data[:duration],
650
- @data[:use_RTH],
651
- @data[:what_to_show].to_s.upcase
652
- ])
653
-
654
- queue.push(@data[:format_date]) if server[:version] > 16
655
-
656
- if contract.sec_type.upcase == "BAG"
657
- queue.concat(contract.serialize_combo_legs)
658
- end
659
-
660
- queue
661
- end
662
- end # RequestHistoricalData
663
-
664
- # data = { :ticker_id => int,
665
- # :contract => Contract,
666
- # :exercise_action => int,
667
- # :exercise_quantity => int,
668
- # :account => string,
669
- # :override => int } ## override? override what?
670
- class ExerciseOptions < AbstractMessage
671
- def self.message_id
672
- 21
673
- end
674
-
675
- def queue(server)
676
-
677
- requireVersion(server, 21)
678
-
679
- q = [self.class.message_id,
680
- 1, # version
681
- @data[:ticker_id]
682
- ]
683
- q.concat(@data[:contract].serialize_long(server[:version]))
684
- q.concat([
685
- @data[:exercise_action],
686
- @data[:exercise_quantity],
687
- @data[:account],
688
- @data[:override]
689
- ])
690
- q
691
- end # queue
692
- end # ExerciseOptions
693
-
694
- # data = { :ticker_id => int,
695
- # :scanner_subscription => ScannerSubscription
696
- # }
697
- class RequestScannerSubscription < AbstractMessage
698
- def self.message_id
699
- 22
700
- end
701
-
702
- def queue(server)
703
- requireVersion(server, 24)
704
-
705
- [
706
- self.class.message_id,
707
- 3, # version
708
- @data[:ticker_id],
709
- @data[:subscription].number_of_rows,
710
- nilFilter(@data[:subscription].number_of_rows),
711
- @data[:subscription].instrument,
712
- @data[:subscription].location_code,
713
- @data[:subscription].scan_code,
714
- nilFilter(@data[:subscription].above_price),
715
- nilFilter(@data[:subscription].below_price),
716
- nilFilter(@data[:subscription].above_volume),
717
- nilFilter(@data[:subscription].market_cap_above),
718
- @data[:subscription].moody_rating_above,
719
- @data[:subscription].moody_rating_below,
720
- @data[:subscription].sp_rating_above,
721
- @data[:subscription].sp_rating_below,
722
- @data[:subscription].maturity_date_above,
723
- @data[:subscription].maturity_date_below,
724
- nilFilter(@data[:subscription].coupon_rate_above),
725
- nilFilter(@data[:subscription].coupon_rate_below),
726
- @data[:subscription].exclude_convertible,
727
- (server[:version] >= 25 ? [ @data[:subscription].average_option_volume_above,
728
- @data[:subscription].scanner_setting_pairs ] : []),
729
-
730
- (server[:version] >= 27 ? [ @data[:subscription].stock_type_filter ] : []),
731
- ].flatten
732
-
733
- end
734
- end # RequestScannerSubscription
735
-
736
-
737
- # data = { :ticker_id => int }
738
- class CancelScannerSubscription
739
- def self.message_id
740
- 23
741
- end
742
-
743
- def queue(server)
744
- requireVersion(server, 24)
745
- [self.class.message_id,
746
- 1, # version
747
- @data[:ticker_id]
748
- ]
749
- end
750
- end # CancelScannerSubscription
751
-
752
-
753
- class RequestScannerParameters
754
- def self.message_id
755
- 24
756
- end
757
-
758
- def queue(server)
759
- requireVersion(server, 24)
760
-
761
- [ self.class.message_id,
762
- 1 # version
763
- ]
764
- end
765
- end # RequestScannerParameters
766
-
767
-
768
- # data = { :ticker_id => int }
769
- class CancelHistoricalData
770
- def self.message_id
771
- 25
772
- end
773
-
774
- def queue(server)
775
- requireVersion(server, 24)
776
- [ self.class.message_id,
777
- 1, # version
778
- @data[:ticker_id]
779
- ]
780
- end
781
- end # CancelHistoricalData
782
-
783
- end # module OutgoingMessages
784
-
785
- ################################################################
786
- #### end outgoing messages
787
- ################################################################
788
-
789
-
790
-
791
- ################################################################
792
- #### Incoming messages
793
- ################################################################
794
-
795
- module IncomingMessages
796
- Classes = Array.new
797
-
798
- #
799
- # This is just a basic generic message from the server.
800
- #
801
- # Class variables:
802
- # @@message_id - integer message id.
803
- #
804
- # Instance attributes:
805
- # :data - Hash of actual data read from a stream.
806
- #
807
- # Override the load(socket) method in your subclass to do actual reading into @data.
808
- #
809
- class AbstractMessage < ExtremelyAbstractMessage
810
- attr_accessor :data
811
-
812
- def self.message_id
813
- raise Exception("AbstractMessage.message_id called - you need to override this in a subclass.")
814
- end
815
-
816
-
817
- def initialize(socket, server_version)
818
- raise Exception.new("Don't use AbstractMessage directly; use the subclass for your specific message type") if self.class.name == "AbstractMessage"
819
- #logger.debug(" * loading #{self.class.name}")
820
- @created_at = Time.now
821
-
822
- @data = Hash.new
823
- @socket = socket
824
- @server_version = server_version
825
-
826
- self.load()
827
-
828
- @socket = nil
829
-
830
-
831
- #logger.debug(" * New #{self.class.name}: #{ self.to_human }")
832
- end
833
-
834
- def AbstractMessage.inherited(by)
835
- super(by)
836
- Classes.push(by)
837
- end
838
-
839
- def load
840
- raise Exception.new("Don't use AbstractMessage; override load() in a subclass.")
841
- end
842
-
843
- protected
844
-
845
- #
846
- # Load @data from the socket according to the given map.
847
- #
848
- # map is a series of Arrays in the format [ [ :name, :type ] ], e.g. autoload([:version, :int ], [:ticker_id, :int])
849
- # type identifiers must have a corresponding read_type method on socket (read_int, etc.).
850
- #
851
- def autoload(*map)
852
- ##logger.debug("autoloading map: " + map.inspect)
853
- map.each { |spec|
854
- @data[spec[0]] = @socket.__send__(("read_" + spec[1].to_s).to_sym)
855
- }
856
- end
857
-
858
- # version_load loads map only if @data[:version] is >= required_version.
859
- def version_load(required_version, *map)
860
- if @data[:version] >= required_version
861
- map.each {|item|
862
- autoload(item)
863
- }
864
- end
865
- end
866
-
867
- end # class AbstractMessage
868
-
869
-
870
- ### Actual message classes
871
-
872
- # The IB code seems to dispatch up to two wrapped objects for this message, a tickPrice
873
- # and sometimes a tickSize, which seems to be identical to the TICK_SIZE object.
874
- #
875
- # Important note from
876
- # http://chuckcaplan.com/twsapi/index.php/void%20tickPrice%28%29 :
877
- #
878
- # "The low you get is NOT the low for the day as you'd expect it
879
- # to be. It appears IB calculates the low based on all
880
- # transactions after 4pm the previous day. The most inaccurate
881
- # results occur when the stock moves up in the 4-6pm aftermarket
882
- # on the previous day and then gaps open upward in the
883
- # morning. The low you receive from TWS can be easily be several
884
- # points different from the actual 9:30am-4pm low for the day in
885
- # cases like this. If you require a correct traded low for the
886
- # day, you can't get it from the TWS API. One possible source to
887
- # help build the right data would be to compare against what Yahoo
888
- # lists on finance.yahoo.com/q?s=ticker under the "Day's Range"
889
- # statistics (be careful here, because Yahoo will use anti-Denial
890
- # of Service techniques to hang your connection if you try to
891
- # request too many bytes in a short period of time from them). For
892
- # most purposes, a good enough approach would start by replacing
893
- # the TWS low for the day with Yahoo's day low when you first
894
- # start watching a stock ticker; let's call this time T. Then,
895
- # update your internal low if the bid or ask tick you receive is
896
- # lower than that for the remainder of the day. You should check
897
- # against Yahoo again at time T+20min to handle the occasional
898
- # case where the stock set a new low for the day in between
899
- # T-20min (the real time your original quote was from, taking into
900
- # account the delay) and time T. After that you should have a
901
- # correct enough low for the rest of the day as long as you keep
902
- # updating based on the bid/ask. It could still get slightly off
903
- # in a case where a short transaction setting a new low appears in
904
- # between ticks of data that TWS sends you. The high is probably
905
- # distorted in the same way the low is, which would throw your
906
- # results off if the stock traded after-hours and gapped down. It
907
- # should be corrected in a similar way as described above if this
908
- # is important to you."
909
- #
910
-
911
- class TickPrice < AbstractMessage
912
- def self.message_id
913
- 1
914
- end
915
-
916
- def load
917
- autoload([:version, :int], [:ticker_id, :int], [:tick_type, :int], [:price, :decimal])
918
-
919
- version_load(2, [:size, :int])
920
- version_load(3, [:can_auto_execute, :int])
921
-
922
- if @data[:version] >= 2
923
- # the IB code translates these into 0, 3, and 5, respectively, and wraps them in a TICK_SIZE-type wrapper.
924
- @data[:type] = case @data[:tick_type]
925
- when 1
926
- :bid
927
- when 2
928
- :ask
929
- when 4
930
- :last
931
- when 6
932
- :high
933
- when 7
934
- :low
935
- when 9
936
- :close
937
- else
938
- nil
939
- end
940
- end
941
-
942
- end # load
943
-
944
- def inspect
945
- "Tick (" + @data[:type].to_s('F') + " at " + @data[:price].to_s('F') + ") " + super.inspect
946
- end
947
-
948
- def to_human
949
- @data[:size].to_s + " " + @data[:type].to_s + " at " + @data[:price].to_s('F')
950
- end
951
-
952
- end # TickPrice
953
-
954
-
955
-
956
- class TickSize < AbstractMessage
957
- def self.message_id
958
- 2
959
- end
960
-
961
- def load
962
- autoload([:version, :int], [:ticker_id, :int], [:tick_type, :int], [:size, :int])
963
- @data[:type] = case @data[:tick_type]
964
- when 0
965
- :bid
966
- when 3
967
- :ask
968
- when 5
969
- :last
970
- when 8
971
- :volume
972
- else
973
- nil
974
- end
975
- end
976
-
977
- def to_human
978
- @data[:type].to_s + " size: " + @data[:size].to_s
979
- end
980
- end # TickSize
981
-
982
-
983
-
984
- class OrderStatus < AbstractMessage
985
- def self.message_id
986
- 3
987
- end
988
-
989
- def load
990
- autoload([:version, :int], [:id, :int], [:status, :string], [:filled, :int], [:remaining, :int],
991
- [:average_fill_price, :decimal])
992
-
993
- version_load(2, [:perm_id, :int])
994
- version_load(3, [:parent_id, :int])
995
- version_load(4, [:last_fill_price, :decimal])
996
- version_load(5, [:client_id, :int])
997
- end
998
- end
999
-
1000
-
1001
- class Error < AbstractMessage
1002
- def self.message_id
1003
- 4
1004
- end
1005
-
1006
- def code
1007
- @data && @data[:code]
1008
- end
1009
-
1010
- def load
1011
- @data[:version] = @socket.read_int
1012
-
1013
- if @data[:version] < 2
1014
- @data[:message] = @socket.read_string
1015
- else
1016
- autoload([:id, :int], [:code, :int], [:message, :string])
1017
- end
1018
- end
1019
-
1020
- def to_human
1021
- "TWS #{@data[:code]}: #{@data[:message]}"
1022
- end
1023
- end # class ErrorMessage
1024
-
1025
- class OpenOrder < AbstractMessage
1026
- attr_accessor :order, :contract
1027
-
1028
- def self.message_id
1029
- 5
1030
- end
1031
-
1032
- def load
1033
- @order = Datatypes::Order.new
1034
- @contract = Datatypes::Contract.new
1035
-
1036
- autoload([:version, :int])
1037
-
1038
- @order.id = @socket.read_int
1039
-
1040
- @contract.symbol = @socket.read_string
1041
- @contract.sec_type = @socket.read_string
1042
- @contract.expiry = @socket.read_string
1043
- @contract.strike = @socket.read_decimal
1044
- @contract.right = @socket.read_string
1045
- @contract.exchange = @socket.read_string
1046
- @contract.currency = @socket.read_string
1047
-
1048
- @contract.local_symbol = @socket.read_string if @data[:version] >= 2
1049
-
1050
- @order.action = @socket.read_string
1051
- @order.total_quantity = @socket.read_int
1052
- @order.order_type = @socket.read_string
1053
- @order.limit_price = @socket.read_decimal
1054
- @order.aux_price = @socket.read_decimal
1055
- @order.tif = @socket.read_string
1056
- @order.oca_group = @socket.read_string
1057
- @order.account = @socket.read_string
1058
- @order.open_close = @socket.read_string
1059
- @order.origin = @socket.read_int
1060
- @order.order_ref = @socket.read_string
1061
-
1062
- @order.client_id = @socket.read_int if @data[:version] >= 3
1063
-
1064
- if @data[:version] >= 4
1065
- @order.perm_id = @socket.read_int
1066
- @order.ignore_rth = (@socket.read_int == 1)
1067
- @order.hidden = (@socket.read_int == 1)
1068
- @order.discretionary_amount = @socket.read_decimal
1069
- end
1070
-
1071
- @order.good_after_time = @socket.read_string if @data[:version] >= 5
1072
- @order.shares_allocation = @socket.read_string if @data[:version] >= 6
1073
-
1074
- if @data[:version] >= 7
1075
- @order.fa_group = @socket.read_string
1076
- @order.fa_method = @socket.read_string
1077
- @order.fa_percentage = @socket.read_string
1078
- @order.fa_profile = @socket.read_string
1079
- end
1080
-
1081
- @order.good_till_date = @socket.read_string if @data[:version] >= 8
1082
-
1083
- if @data[:version] >= 9
1084
- @order.rule_80A = @socket.read_string
1085
- @order.percent_offset = @socket.read_decimal
1086
- @order.settling_firm = @socket.read_string
1087
- @order.short_sale_slot = @socket.read_int
1088
- @order.designated_location = @socket.read_string
1089
- @order.auction_strategy = @socket.read_int
1090
- @order.starting_price = @socket.read_decimal
1091
- @order.stock_ref_price = @socket.read_decimal
1092
- @order.delta = @socket.read_decimal
1093
- @order.stock_range_lower = @socket.read_decimal
1094
- @order.stock_range_upper = @socket.read_decimal
1095
- @order.display_size = @socket.read_int
1096
- @order.rth_only = @socket.read_boolean
1097
- @order.block_order = @socket.read_boolean
1098
- @order.sweep_to_fill = @socket.read_boolean
1099
- @order.all_or_none = @socket.read_boolean
1100
- @order.min_quantity = @socket.read_int
1101
- @order.oca_type = @socket.read_int
1102
- @order.eTrade_only = @socket.read_boolean
1103
- @order.firm_quote_only = @socket.read_boolean
1104
- @order.nbbo_price_cap = @socket.read_decimal
1105
- end
1106
-
1107
- if @data[:version] >= 10
1108
- @order.parent_id = @socket.read_int
1109
- @order.trigger_method = @socket.read_int
1110
- end
1111
-
1112
- if @data[:version] >= 11
1113
- @order.volatility = @socket.read_decimal
1114
- @order.volatility_type = @socket.read_int
1115
-
1116
- if @data[:version] == 11
1117
- @order.delta_neutral_order_type = ( @socket.read_int == 0 ? "NONE" : "MKT" )
1118
- else
1119
- @order.delta_neutral_order_type = @socket.read_string
1120
- @order.delta_neutral_aux_price = @socket.read_decimal
1121
- end
1122
-
1123
- @order.continuous_update = @socket.read_int
1124
- if @server_version == 26
1125
- @order.stock_range_lower = @socket.read_decimal
1126
- @order.stock_range_upper = @socket.read_decimal
1127
- end
1128
-
1129
- @order.reference_price_type = @socket.read_int
1130
- end # if version >= 11
1131
-
1132
-
1133
- end # load
1134
- end # OpenOrder
1135
-
1136
- class AccountValue < AbstractMessage
1137
- def self.message_id
1138
- 6
1139
- end
1140
-
1141
- def load
1142
- autoload([:version, :int], [:key, :string], [:value, :string], [:currency, :string])
1143
- version_load(2, [:account_name, :string])
1144
- end
1145
-
1146
- def to_human
1147
- "<AccountValue: acct ##{@data[:account_name]}; #{@data[:key]}=#{@data[:value]} (#{@data[:currency]})>"
1148
- end
1149
- end # AccountValue
1150
-
1151
- class PortfolioValue < AbstractMessage
1152
- attr_accessor :contract
1153
-
1154
- def self.message_id
1155
- 7
1156
- end
1157
-
1158
- def load
1159
- @contract = Datatypes::Contract.new
1160
-
1161
- autoload([:version, :int])
1162
- @contract.symbol = @socket.read_string
1163
- @contract.sec_type = @socket.read_string
1164
- @contract.expiry = @socket.read_string
1165
- @contract.strike = @socket.read_decimal
1166
- @contract.right = @socket.read_string
1167
- @contract.currency = @socket.read_string
1168
- @contract.local_symbol = @socket.read_string if @data[:version] >= 2
1169
-
1170
- autoload([:position, :int], [:market_price, :decimal], [:market_value, :decimal])
1171
- version_load(3, [:average_cost, :decimal], [:unrealized_pnl, :decimal], [:realized_pnl, :decimal])
1172
- version_load(4, [:account_name, :string])
1173
- end
1174
-
1175
- def to_human
1176
- "<PortfolioValue: update for #{@contract.to_human}: market price #{@data[:market_price].to_s('F')}; market value " +
1177
- "#{@data[:market_value].to_s('F')}; position #{@data[:position]}; unrealized PnL #{@data[:unrealized_pnl].to_s('F')}; " +
1178
- "realized PnL #{@data[:realized_pnl].to_s('F')}; account #{@data[:account_name]}>"
1179
- end
1180
-
1181
- end # PortfolioValue
1182
-
1183
- class AccountUpdateTime < AbstractMessage
1184
- def self.message_id
1185
- 8
1186
- end
1187
-
1188
- def load
1189
- autoload([:version, :int], [:time_stamp, :string])
1190
- end
1191
- end # AccountUpdateTime
1192
-
1193
-
1194
- #
1195
- # This message is always sent by TWS automatically at connect.
1196
- # The IB class subscribes to it automatically and stores the order id in
1197
- # its :next_order_id attribute.
1198
- #
1199
- class NextValidID < AbstractMessage
1200
- def self.message_id
1201
- 9
1202
- end
1203
-
1204
- def load
1205
- autoload([:version, :int], [:order_id, :int])
1206
- end
1207
-
1208
- end # NextValidIDMessage
1209
-
1210
-
1211
- class ContractData < AbstractMessage
1212
- attr_accessor :contract_details
1213
-
1214
- def self.message_id
1215
- 10
1216
- end
1217
-
1218
- def load
1219
- @contract_details = Datatypes::ContractDetails.new
1220
-
1221
- autoload([:version, :int])
1222
-
1223
- @contract_details.summary.symbol = @socket.read_string
1224
- @contract_details.summary.sec_type = @socket.read_string
1225
- @contract_details.summary.expiry = @socket.read_string
1226
- @contract_details.summary.strike = @socket.read_decimal
1227
- @contract_details.summary.right = @socket.read_string
1228
- @contract_details.summary.exchange = @socket.read_string
1229
- @contract_details.summary.currency = @socket.read_string
1230
- @contract_details.summary.local_symbol = @socket.read_string
1231
-
1232
- @contract_details.market_name = @socket.read_string
1233
- @contract_details.trading_class = @socket.read_string
1234
- @contract_details.con_id = @socket.read_int
1235
- @contract_details.min_tick = @socket.read_decimal
1236
- @contract_details.multiplier = @socket.read_string
1237
- @contract_details.order_types = @socket.read_string
1238
- @contract_details.valid_exchanges = @socket.read_string
1239
- @contract_details.price_magnifier = @socket.read_int if @data[:version] >= 2
1240
-
1241
- end
1242
- end # ContractData
1243
-
1244
-
1245
- class ExecutionData < AbstractMessage
1246
- attr_accessor :contract, :execution
1247
-
1248
- def self.message_id
1249
- 11
1250
- end
1251
-
1252
- def load
1253
- @contract = Datatypes::Contract.new
1254
- @execution = Datatypes::Execution.new
1255
-
1256
- autoload([:version, :int], [:order_id, :int])
1257
-
1258
- @contract.symbol = @socket.read_string
1259
- @contract.sec_type = @socket.read_string
1260
- @contract.expiry = @socket.read_string
1261
- @contract.strike = @socket.read_decimal
1262
- @contract.right = @socket.read_string
1263
- @contract.currency = @socket.read_string
1264
- @contract.local_symbol = @socket.read_string if @data[:version] >= 2
1265
-
1266
- @execution.order_id = @data[:order_id]
1267
- @execution.exec_id = @socket.read_string
1268
- @execution.time = @socket.read_string
1269
- @execution.account_number = @socket.read_string
1270
- @execution.exchange = @socket.read_string
1271
- @execution.side = @socket.read_string
1272
- @execution.shares = @socket.read_int
1273
- @execution.price = @socket.read_decimal
1274
-
1275
- @execution.perm_id = @socket.read_int if @data[:version] >= 2
1276
- @execution.client_id = @socket.read_int if @data[:version] >= 3
1277
- @execution.liquidation = @socket.read_int if @data[:version] >= 4
1278
- end
1279
- end # ExecutionData
1280
-
1281
- class MarketDepth < AbstractMessage
1282
- def self.message_id
1283
- 12
1284
- end
1285
-
1286
- def load
1287
- autoload([:version, :int], [:id, :int], [:position, :int], [:operation, :int], [:side, :int], [:price, :decimal], [:size, :int])
1288
- end
1289
- end # MarketDepth
1290
-
1291
- class MarketDepthL2 < AbstractMessage
1292
- def self.message_id
1293
- 13
1294
- end
1295
-
1296
- def load
1297
- autoload([:version, :int], [:id, :int], [:position, :int], [:market_maker, :string], [:operation, :int], [:side, :int],
1298
- [:price, :decimal], [:size, :int])
1299
- end
1300
- end # MarketDepthL2
1301
-
1302
-
1303
- class NewsBulletins < AbstractMessage
1304
- def self.message_id
1305
- 14
1306
- end
1307
-
1308
- def load
1309
- autoload([:version, :int], [:news_message_id, :int], [:news_message_type, :int], [:news_message, :string], [:originating_exchange, :string])
1310
- end
1311
- end # NewsBulletins
1312
-
1313
- class ManagedAccounts < AbstractMessage
1314
- def self.message_id
1315
- 15
1316
- end
1317
-
1318
- def load
1319
- autoload([:version, :int], [:accounts_list, :string])
1320
- end
1321
- end # ManagedAccounts
1322
-
1323
- # "Fa"?
1324
- class ReceiveFa < AbstractMessage
1325
- def self.message_id
1326
- 16
1327
- end
1328
-
1329
- def load
1330
- autoload([:version, :int], [:fa_data_type, :int], [:xml, :string])
1331
- end
1332
- end # ReceiveFa
1333
-
1334
- class HistoricalData < AbstractMessage
1335
- def self.message_id
1336
- 17
1337
- end
1338
-
1339
- def load
1340
- autoload([:version, :int], [:req_id, :int])
1341
- version_load(2, [:start_date_str, :string], [:end_date_str, :string])
1342
- @data[:completed_indicator] = "finished-" + @data[:start_date_str] + "-" + @data[:end_date_str] if @data[:version] >= 2
1343
-
1344
- autoload([:item_count, :int])
1345
- @data[:history] = Array.new(@data[:item_count]) {|index|
1346
- attrs = {
1347
- :date => @socket.read_string,
1348
- :open => @socket.read_decimal,
1349
- :high => @socket.read_decimal,
1350
- :low => @socket.read_decimal,
1351
- :close => @socket.read_decimal,
1352
- :volume => @socket.read_int,
1353
- :wap => @socket.read_decimal,
1354
- :has_gaps => @socket.read_string
1355
- }
1356
-
1357
- Datatypes::Bar.new(attrs)
1358
- }
1359
-
1360
- end
1361
-
1362
- def to_human
1363
- "<HistoricalData: req id #{@data[:req_id]}, #{@data[:item_count]} items, from #{@data[:start_date_str]} to #{@data[:end_date_str]}>"
1364
- end
1365
- end # HistoricalData
1366
-
1367
- class BondContractData < AbstractMessage
1368
- attr_accessor :contract_details
1369
- def self.message_id
1370
- 18
1371
- end
1372
-
1373
- def load
1374
- @contract_details = Datatypes::ContractDetails.new
1375
- @contract_details.summary.symbol = @socket.read_string
1376
- @contract_details.summary.sec_type = @socket.read_string
1377
- @contract_details.summary.cusip = @socket.read_string
1378
- @contract_details.summary.coupon = @socket.read_decimal
1379
- @contract_details.summary.maturity = @socket.read_string
1380
- @contract_details.summary.issue_date = @socket.read_string
1381
- @contract_details.summary.ratings = @socket.read_string
1382
- @contract_details.summary.bond_type = @socket.read_string
1383
- @contract_details.summary.coupon_type = @socket.read_string
1384
- @contract_details.summary.convertible = @socket.read_boolean
1385
- @contract_details.summary.callable = @socket.read_boolean
1386
- @contract_details.summary.puttable = @socket.read_boolean
1387
- @contract_details.summary.desc_append = @socket.read_string
1388
- @contract_details.summary.exchange = @socket.read_string
1389
- @contract_details.summary.currency = @socket.read_string
1390
- @contract_details.market_name = @socket.read_string
1391
- @contract_details.trading_class = @socket.read_string
1392
- @contract_details.con_id = @socket.read_int
1393
- @contract_details.min_tick = @socket.read_decimal
1394
- @contract_details.order_types = @socket.read_string
1395
- @contract_details.valid_exchanges = @socket.read_string
1396
-
1397
- end
1398
- end # BondContractData
1399
-
1400
- class ScannerParameters < AbstractMessage
1401
- def self.message_id
1402
- 19
1403
- end
1404
-
1405
- def load
1406
- autoload([:version, :int], [:xml, :string])
1407
- end
1408
- end # ScannerParamters
1409
-
1410
-
1411
- class ScannerData < AbstractMessage
1412
- attr_accessor :contract_details
1413
- def self.message_id
1414
- 20
1415
- end
1416
-
1417
- def load
1418
- autoload([:version, :int], [:ticker_id, :int], [:number_of_elements, :int])
1419
- @data[:results] = Array.new(@data[:number_of_elements]) { |index|
1420
- {
1421
- :rank => @socket.read_int
1422
- ## TODO: Pick up here.
1423
- }
1424
- }
1425
-
1426
- end
1427
- end # ScannerData
1428
-
1429
- ###########################################
1430
- ###########################################
1431
- ## End message classes
1432
- ###########################################
1433
- ###########################################
1434
-
1435
- Table = Hash.new
1436
- Classes.each { |msg_class|
1437
- Table[msg_class.message_id] = msg_class
1438
- }
1439
-
1440
- #logger.debug("Incoming message class table is #{Table.inspect}")
1441
-
1442
- end # module IncomingMessages
1443
- ################################################################
1444
- #### End incoming messages
1445
- ################################################################
1446
-
1447
-
1448
-
1449
- end # module IB
9
+ module IB
10
+ IncomingMessages = Messages::Incoming # Legacy alias
11
+ OutgoingMessages = Messages::Outgoing # Legacy alias
12
+ end