wdevauld-ib-ruby 0.2

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