ib-ruby 0.4.3
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/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
|
+
|