wdevauld-ib-ruby 0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Manifest.txt ADDED
@@ -0,0 +1,20 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/AccountInfo
7
+ bin/HistoricToCSV
8
+ bin/RequestHistoricData
9
+ bin/RequestMarketData
10
+ bin/SimpleTimeAndSales
11
+ bin/ib-ruby
12
+ lib/ib-ruby.rb
13
+ lib/ib-ruby/datatypes.rb
14
+ lib/ib-ruby/ib.rb
15
+ lib/ib-ruby/messages.rb
16
+ lib/ib-ruby/symbols/forex.rb
17
+ lib/ib-ruby/symbols/futures.rb
18
+ spec/ib-ruby_spec.rb
19
+ spec/spec_helper.rb
20
+ test/test_ib-ruby.rb
data/README.txt ADDED
@@ -0,0 +1,50 @@
1
+ ib-ruby
2
+ By Wes Devauld (wes at devauld dot ca)
3
+ http://github.com/wdevauld/ib-ruby
4
+
5
+ This is a fork of Paul Legato's (pjlegato at gmail dot com) work found at:
6
+ http://github.com/pjlegato/ib-ruby
7
+
8
+ Copyright (C) 2009 Wes Devauld
9
+
10
+ == DESCRIPTION:
11
+
12
+ * Ruby Implementation of the Interactive Broker' TWS API
13
+
14
+ == FEATURES/PROBLEMS:
15
+
16
+ * This is a ALPHA release, and should not be used for live trading. Any features contained with are AS-IS and may not work in all conditions
17
+ * This code is not sanctioned by Interactive Brokers
18
+ * TODO Deal with Logging properly
19
+ == SYNOPSIS:
20
+
21
+ First, start up Interactive Broker's Trader Work Station. Ensure it is configured to allow API connections on localhost
22
+
23
+ >> require 'ib-ruby'
24
+ >> ib_connection = IB:IB.new()
25
+
26
+ == REQUIREMENTS:
27
+
28
+ * FIXME List all the requirements
29
+
30
+ == INSTALL:
31
+
32
+ * Ensure that http://gems.github.com is in your gem sources
33
+ * sudo gem install wdevauld-ib-ruby
34
+
35
+ == LICENSE:
36
+
37
+ This library is free software; you can redistribute it and/or modify
38
+ it under the terms of the GNU Lesser General Public License as
39
+ published by the Free Software Foundation; either version 2.1 of the
40
+ License, or (at your option) any later version.
41
+
42
+ This library is distributed in the hope that it will be useful, but
43
+ WITHOUT ANY WARRANTY; without even the implied warranty of
44
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
45
+ Lesser General Public License for more details.
46
+
47
+ You should have received a copy of the GNU Lesser General Public
48
+ License along with this library; if not, write to the Free Software
49
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
50
+ 02110-1301 USA
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ # This library is free software; you can redistribute it and/or
2
+ # modify it under the terms of the GNU Lesser General Public
3
+ # License see README.txt for more details
4
+
5
+ # Look in the tasks/setup.rb file for the various options that can be
6
+ # configured in this Rakefile. The .rake files in the tasks directory
7
+ # are where the options are used.
8
+
9
+ begin
10
+ require 'bones'
11
+ Bones.setup
12
+ rescue LoadError
13
+ begin
14
+ load 'tasks/setup.rb'
15
+ rescue LoadError
16
+ raise RuntimeError, '### please install the "bones" gem ###'
17
+ end
18
+ end
19
+
20
+ ensure_in_path 'lib'
21
+ require 'ib-ruby'
22
+
23
+ task :default => 'spec:run'
24
+
25
+ PROJ.name = 'ib-ruby'
26
+ PROJ.authors = 'Wes Devauld'
27
+ PROJ.email = 'wes@devauld.ca'
28
+ PROJ.url = 'http://github.com/wdevauld/ib-ruby/tree/master'
29
+ PROJ.version = IbRuby::VERSION
30
+
31
+ PROJ.spec.opts << '--color'
32
+
33
+ # EOF
data/bin/AccountInfo ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby -w
2
+ #
3
+ # Copyright (C) 2007 Paul Legato.
4
+ #
5
+ # This library is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as
7
+ # published by the Free Software Foundation; either version 2.1 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful, but
11
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
+ # 02110-1301 USA
19
+ #
20
+
21
+ $:.push(File.dirname(__FILE__) + "/../")
22
+
23
+ require 'ib'
24
+ require 'datatypes'
25
+ require 'symbols/futures'
26
+
27
+ # First, connect to IB TWS.
28
+ ib = IB::IB.new
29
+
30
+ # Uncomment this for verbose debug messages:
31
+ # IB::IBLogger.level = Logger::Severity::DEBUG
32
+
33
+ ## Subscribe to the messages that TWS sends in response to a request
34
+ ## for account data.
35
+
36
+ ib.subscribe(IB::IncomingMessages::AccountValue, lambda {|msg|
37
+ puts msg.to_human
38
+ })
39
+
40
+ ib.subscribe(IB::IncomingMessages::PortfolioValue, lambda {|msg|
41
+ puts msg.to_human
42
+ })
43
+
44
+ ib.subscribe(IB::IncomingMessages::AccountUpdateTime, lambda {|msg|
45
+ puts msg.to_human
46
+ })
47
+
48
+
49
+
50
+ msg = IB::OutgoingMessages::RequestAccountData.new({
51
+ :subscribe => true,
52
+ :account_code => ''
53
+ })
54
+ ib.dispatch(msg)
55
+
56
+
57
+ puts "\n\n\t******** Press <Enter> to quit.. *********\n\n"
58
+
59
+ gets
60
+
61
+ puts "Cancelling account data subscription.."
62
+
63
+ msg = IB::OutgoingMessages::RequestAccountData.new({
64
+ :subscribe => false,
65
+ :account_code => ''
66
+ })
67
+ ib.dispatch(msg)
68
+
69
+
70
+ puts "Done."
71
+
data/bin/HistoricToCSV ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env ruby -w
2
+ #
3
+ # Copyright (C) 2007 Paul Legato.
4
+ #
5
+ # This library is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as
7
+ # published by the Free Software Foundation; either version 2.1 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful, but
11
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
+ # 02110-1301 USA
19
+ #
20
+ #####################################################################
21
+ #
22
+ # This program demonstrates how to download historic data and write it
23
+ # to a CSV file.
24
+ #
25
+ # To use, set CSV_FILE to the file you want to write (which will be
26
+ # overwritten automatically if it already exists), set the contract
27
+ # data, duration, data type (trades, bid, ask, midpoint), etc. as you
28
+ # like, and run the program.
29
+ #
30
+ # Note that it does not detect when the historic data from the server
31
+ # has stopped arriving automatically. This limitation will be
32
+ # addressed soon. For now, just press <Enter> when the data stream on
33
+ # the console stops, and the output file will be closed and the
34
+ # program terminated.
35
+ #
36
+
37
+ $:.push(File.dirname(__FILE__) + "/../")
38
+
39
+ require 'ib'
40
+ require 'datatypes'
41
+ require 'symbols/futures'
42
+
43
+
44
+ ### Configurable Options
45
+
46
+ # if Quiet == false, status data will be printed to STDERR
47
+ Quiet = false
48
+
49
+ # How long to wait when no messages are received from TWS before
50
+ # exiting, in seconds
51
+ Timeout = 2
52
+
53
+ SymbolToRequest = IB::Symbols::Futures[:eur]
54
+
55
+ ### end options
56
+
57
+
58
+ #
59
+ # Definition of what we want market data for. We have to keep track
60
+ # of what ticker id corresponds to what symbol ourselves, because the
61
+ # ticks don't include any other identifying information.
62
+ #
63
+ # The choice of ticker ids is, as far as I can tell, arbitrary.
64
+ #
65
+ # Note that as of 4/07 there is no historical data available for forex spot.
66
+ #
67
+ @market =
68
+ {
69
+ 123 => SymbolToRequest
70
+ }
71
+
72
+ # To determine when the timeout has passed.
73
+ @last_msg_time = Time.now.to_i + 2
74
+
75
+ # Connect to IB TWS.
76
+ ib = IB::IB.new
77
+
78
+ # Uncomment this for verbose debug messages:
79
+ # IB::IBLogger.level = Logger::Severity::DEBUG
80
+
81
+ #
82
+ # Now, subscribe to HistoricalData incoming events. The code
83
+ # passed in the block will be executed when a message of that type is
84
+ # received, with the received message as its argument. In this case,
85
+ # we just print out the data.
86
+ #
87
+ # Note that we have to look the ticker id of each incoming message
88
+ # up in local memory to figure out what it's for.
89
+ #
90
+ # (N.B. The description field is not from IB TWS. It is defined
91
+ # locally in forex.rb, and is just arbitrary text.)
92
+
93
+
94
+ ib.subscribe(IB::IncomingMessages::HistoricalData, lambda {|msg|
95
+
96
+ STDERR.puts @market[msg.data[:req_id]].description + ": " + msg.data[:item_count].to_s + " items:" unless Quiet
97
+
98
+ msg.data[:history].each { |datum|
99
+
100
+ @last_msg_time = Time.now.to_i
101
+
102
+ STDERR.puts " " + datum.to_s unless Quiet
103
+ STDOUT.puts "#{datum.date},#{datum.open.to_digits},#{datum.high.to_digits},#{datum.low.to_digits},#{datum.close.to_digits},#{datum.volume}"
104
+ }
105
+ })
106
+
107
+ # Now we actually request historical data for the symbols we're
108
+ # interested in. TWS will respond with a HistoricalData message,
109
+ # which will be received by the code above.
110
+
111
+ @market.each_pair {|id, contract|
112
+ msg = IB::OutgoingMessages::RequestHistoricalData.new({
113
+ :ticker_id => id,
114
+ :contract => contract,
115
+ :end_date_time => Time.now.to_ib,
116
+ :duration => (60 * 60 * 24).to_s, # how long before end_date_time to request in seconds - this means 1 day
117
+ :bar_size => IB::OutgoingMessages::RequestHistoricalData::BarSizes.index(:five_minutes),
118
+ :what_to_show => :trades,
119
+ :use_RTH => 0,
120
+ :format_date => 2
121
+ })
122
+ ib.dispatch(msg)
123
+ }
124
+
125
+
126
+ while true
127
+ exit(0) if Time.now.to_i > @last_msg_time + Timeout
128
+ sleep 1
129
+ end
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env ruby -w
2
+ #
3
+ # Copyright (C) 2007-8 Paul Legato. pjlegato at gmail dot com.
4
+ #
5
+ # This library is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU Lesser General Public License as
7
+ # published by the Free Software Foundation; either version 2.1 of the
8
+ # License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful, but
11
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
+ # 02110-1301 USA
19
+ #
20
+ # >> YOUR USE OF THIS PROGRAM IS ENTIRELY AT YOUR OWN RISK. <<
21
+ # >> IT MAY CONTAIN POTENTIALLY COSTLY BUGS, ERRORS, ETC., BOTH KNOWN AND UNKNOWN. <<
22
+ #
23
+
24
+ $:.push(File.dirname(__FILE__) + "/../")
25
+
26
+ # IB-Ruby libraries
27
+ require 'ib'
28
+ require 'datatypes'
29
+ require 'symbols/futures'
30
+
31
+ # Stdlib
32
+ require 'time' # for extended time parsing
33
+
34
+ # Gems - requires duration and getopt.
35
+ require 'rubygems'
36
+ require 'duration'
37
+ require 'getopt/long'
38
+
39
+
40
+ require "getopt/long"
41
+ include Getopt
42
+ opt = Getopt::Long.getopts(
43
+ ["--help", BOOLEAN],
44
+ ["--end", REQUIRED],
45
+ ["--security", REQUIRED],
46
+ ["--duration", REQUIRED],
47
+ ["--barsize", REQUIRED],
48
+ ["--header",BOOLEAN],
49
+ ["--dateformat", REQUIRED],
50
+ ["--nonregularhours", BOOLEAN],
51
+ ["--verbose", BOOLEAN],
52
+ ["--veryverbose", BOOLEAN]
53
+ )
54
+
55
+ if opt["help"] || opt["security"].nil? || opt["security"].empty?
56
+ puts <<ENDHELP
57
+
58
+ ** RequestHistoricData.rb - Copyright (C) 2007-8 Paul Legato.
59
+
60
+ This library is free software; you can redistribute it and/or modify
61
+ it under the terms of the GNU Lesser General Public License as
62
+ published by the Free Software Foundation; either version 2.1 of the
63
+ License, or (at your option) any later version.
64
+
65
+ This library is distributed in the hope that it will be useful, but
66
+ WITHOUT ANY WARRANTY; without even the implied warranty of
67
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
68
+ Lesser General Public License for more details.
69
+
70
+ You should have received a copy of the GNU Lesser General Public
71
+ License along with this library; if not, write to the Free Software
72
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
73
+ 02110-1301 USA
74
+
75
+ The author and this software are not connected with Interactive
76
+ Brokers in any way, nor do they endorse us.
77
+
78
+ ************************************************************************************
79
+
80
+ >> YOUR USE OF THIS PROGRAM IS ENTIRELY AT YOUR OWN RISK. <<
81
+ >> IT MAY CONTAIN POTENTIALLY COSTLY BUGS, ERRORS, ETC., BOTH KNOWN AND UNKNOWN. <<
82
+ >> DO NOT USE THIS SOFTWARE IF YOU ARE UNWILLING TO ACCEPT ALL RISK IN DOING SO. <<
83
+
84
+ ************************************************************************************
85
+
86
+
87
+ This program requires a TWS running on localhost on the standard port
88
+ that uses API protocol version 15 or higher. Any modern TWS should
89
+ work. (Patches to make it work on an arbitrary host/port are welcome.)
90
+
91
+ ----------
92
+
93
+ One argument is required: --security, the security specification you want, in
94
+ "long serialized IB-Ruby" format. This is a colon-separated string of the format:
95
+
96
+ symbol:security_type:expiry:strike:right:multiplier:exchange:primary_exchange:currency:local_symbol
97
+
98
+ Fields not needed for a particular security should be left blank (e.g. strike and right are only relevant for options.)
99
+
100
+ For example, to query the British pound futures contract trading on Globex expiring in September, 2008,
101
+ the correct command line is:
102
+
103
+ ./RequestHistoricData.rb --security GBP:FUT:200809:::62500:GLOBEX::USD:
104
+
105
+ Consult datatypes.rb for allowed values, and see also the examples in the symbols/ directory (load them in
106
+ irb and run security#serialize_ib_ruby(ib_version) to see the appropriate string.)
107
+
108
+ ***
109
+
110
+ Options:
111
+
112
+ --end is is the last time we want data for. The default is now.
113
+ This is eval'ed by Ruby, so you can use a Ruby expression, which must return a Time object.
114
+
115
+
116
+ --duration is how much historic data we want, in seconds, before --end's time.
117
+ The default is 86400 (seconds, which is 1 day.)
118
+ The TWS-imposed limit is 86400 (1 day per request.) Requests for more than 86400 seconds worth of historic data will fail.
119
+
120
+ --what determines what the data will be comprised of. This can be "trades", "midpoint", "bid", or "asked".
121
+ The default is "trades".
122
+
123
+ --barsize determines how long each bar will be.
124
+
125
+ Possible values (from the IB documentation):
126
+
127
+ 1 = 1 sec
128
+ 2 = 5 sec
129
+ 3 = 15 sec
130
+ 4 = 30 sec
131
+ 5 = 1 minute
132
+ 6 = 2 minutes
133
+ 7 = 5 minutes
134
+ 8 = 15 minutes
135
+ 9 = 30 minutes
136
+ 10 = 1 hour
137
+ 11 = 1 day
138
+
139
+ Values less than 4 do not appear to work for some securities.
140
+ The default is 8, 15 minutes.
141
+
142
+ --nonregularhours :
143
+ Normally, only data from the instrument's regular trading hours is returned.
144
+ If --nonregularhours is given, all data available during the time
145
+ span requested is returned, even data bars covering time
146
+ intervals where the market in question was illiquid. If
147
+
148
+
149
+ --dateformat : a --dateformat of 1 will cause the dates in the returned
150
+ messages with the historic data to be in a text format, like
151
+ "20050307 11:32:16". If you set it to 2 instead, you
152
+ will get an offset in seconds from the beginning of 1970, which
153
+ is the same format as the UNIX epoch time.
154
+
155
+ The default is 1 (human-readable time.)
156
+
157
+ --header : if present, prints a 1 line CSV header describing the fields in the CSV.
158
+
159
+ --veryverbose : if present, prints very verbose debugging info.
160
+ --verbose : if present, prints all messages received from IB, and print the data in human-readable
161
+ format.
162
+
163
+ Otherwise, in the default mode, prints only the historic data (and any errors), and prints the
164
+ data in CSV format.
165
+
166
+ ENDHELP
167
+ #' <- fix broken syntax highlighting in Aquamacs
168
+ exit
169
+
170
+ end
171
+
172
+ ### Parameters
173
+
174
+ # DURATION is how much historic data we want, in seconds, before END_DATE_TIME.
175
+ # (The 'duration' gem gives us methods like #hour on integers.)
176
+ DURATION = (opt["duration"] && opt["duration"].to_i) || 1.day
177
+
178
+ if DURATION > 86400
179
+ STDERR.puts("\nTWS does not accept a --duration longer than 86400 seconds (1 day.) Please try again with a smaller duration.\n\n")
180
+ exit(1)
181
+ end
182
+
183
+
184
+ # This is the last time we want data for.
185
+ END_DATE_TIME = (opt["end"] && eval(opt["end"]).to_ib) || Time.now.to_ib
186
+
187
+
188
+ # This can be :trades, :midpoint, :bid, or :asked
189
+ WHAT = (opt["what"] && opt["what"].to_sym) || :trades
190
+
191
+ # Possible bar size values:
192
+ # 1 = 1 sec
193
+ # 2 = 5 sec
194
+ # 3 = 15 sec
195
+ # 4 = 30 sec
196
+ # 5 = 1 minute
197
+ # 6 = 2 minutes
198
+ # 7 = 5 minutes
199
+ # 8 = 15 minutes
200
+ # 9 = 30 minutes
201
+ # 10 = 1 hour
202
+ # 11 = 1 day
203
+ #
204
+ # Values less than 4 do not appear to actually work; they are rejected by the server.
205
+ #
206
+ BAR_SIZE = (opt["barsize"] && opt["barsize"].to_i) || 8
207
+
208
+ # If REGULAR_HOURS_ONLY is set to 0, all data available during the time
209
+ # span requested is returned, even data bars covering time
210
+ # intervals where the market in question was illiquid. If useRTH
211
+ # has a non-zero value, only data within the "Regular Trading
212
+ # Hours" of the product in question is returned, even if the time
213
+ # span requested falls partially or completely outside of them.
214
+
215
+ REGULAR_HOURS_ONLY = opt["nonregularhours"] ? 0 : 1
216
+
217
+ # Using a DATE_FORMAT of 1 will cause the dates in the returned
218
+ # messages with the historic data to be in a text format, like
219
+ # "20050307 11:32:16". If you set :format_date to 2 instead, you
220
+ # will get an offset in seconds from the beginning of 1970, which
221
+ # is the same format as the UNIX epoch time.
222
+
223
+ DATE_FORMAT = (opt["dateformat"] && opt["dateformat"].to_i) || 1
224
+
225
+ VERYVERBOSE = !opt["veryverbose"].nil?
226
+ VERBOSE = !opt["verbose"].nil?
227
+
228
+ #
229
+ # Definition of what we want market data for. We have to keep track
230
+ # of what ticker id corresponds to what symbol ourselves, because the
231
+ # ticks don't include any other identifying information.
232
+ #
233
+ # The choice of ticker ids is, as far as I can tell, arbitrary.
234
+ #
235
+ # Note that as of 4/07 there is no historical data available for forex spot.
236
+ #
237
+ @market =
238
+ {
239
+ 123 => opt["security"]
240
+ }
241
+
242
+
243
+ # First, connect to IB TWS.
244
+ ib = IB::IB.new
245
+
246
+
247
+ # Default level is quiet, only warnings printed.
248
+ IB::IBLogger.level = Logger::Severity::ERROR
249
+
250
+ # For verbose printing of each message:
251
+ IB::IBLogger.level = Logger::Severity::INFO if VERBOSE
252
+
253
+ # For very verbose debug messages:
254
+ IB::IBLogger.level = Logger::Severity::DEBUG if VERYVERBOSE
255
+
256
+ puts "datetime,open,high,low,close,volume,wap,has_gaps" if !opt["header"].nil?
257
+
258
+ lastMessageTime = Queue.new # for communicating with the reader thread.
259
+
260
+ #
261
+ # Subscribe to incoming HistoricalData events. The code passed in the
262
+ # block will be executed when a message of the subscribed type is
263
+ # received, with the received message as its argument. In this case,
264
+ # we just print out the data.
265
+ #
266
+ # Note that we have to look the ticker id of each incoming message
267
+ # up in local memory to figure out what security it relates to.
268
+ # The incoming message packet from TWS just identifies it by ticker id.
269
+ #
270
+ ib.subscribe(IB::IncomingMessages::HistoricalData, lambda {|msg|
271
+ STDERR.puts @market[msg.data[:req_id]].description + ": " + msg.data[:item_count].to_s + " items:" if VERBOSE
272
+
273
+ msg.data[:history].each { |datum|
274
+ puts(if VERBOSE
275
+ datum.to_s
276
+ else
277
+ "#{datum.date},#{datum.open.to_digits},#{datum.high.to_digits},#{datum.low.to_digits}," +
278
+ "#{datum.close.to_digits},#{datum.volume},#{datum.wap.to_digits},#{datum.has_gaps}"
279
+ end
280
+ )
281
+ }
282
+ lastMessageTime.push(Time.now)
283
+ })
284
+
285
+ # Now we actually request historical data for the symbols we're
286
+ # interested in. TWS will respond with a HistoricalData message,
287
+ # which will be received by the code above.
288
+
289
+ @market.each_pair {|id, contract|
290
+ msg = IB::OutgoingMessages::RequestHistoricalData.new({
291
+ :ticker_id => id,
292
+ :contract => contract,
293
+ :end_date_time => END_DATE_TIME,
294
+ :duration => DURATION, # seconds == 1 hour
295
+ :bar_size => BAR_SIZE, # 1 minute bars
296
+ :what_to_show => WHAT,
297
+ :use_RTH => REGULAR_HOURS_ONLY,
298
+ :format_date => DATE_FORMAT
299
+ })
300
+ ib.dispatch(msg)
301
+ }
302
+
303
+
304
+ # A complication here is that IB does not send any indication when all historic data is done being delivered.
305
+ # So we have to guess - when there is no more new data for some period, we interpret that as "end of data" and exit.
306
+
307
+ while true
308
+ lastTime = lastMessageTime.pop # blocks until a message is ready on the queue
309
+ sleep 2 # .. wait ..
310
+ exit if lastMessageTime.empty? # if still no more messages after 2 more seconds, exit.
311
+ end
312
+