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