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/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
+
data/bin/ib-ruby ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib ib-ruby]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF