ib-ruby 0.4.3 → 0.4.20

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