ib-ruby 0.6.1 → 0.7.0
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 +4 -0
- data/README.md +25 -17
- data/VERSION +1 -1
- data/bin/account_info +1 -1
- data/bin/cancel_orders +2 -1
- data/bin/contract_details +3 -2
- data/bin/depth_of_market +1 -1
- data/bin/historic_data +1 -1
- data/bin/historic_data_cli +57 -104
- data/bin/list_orders +4 -3
- data/bin/market_data +1 -1
- data/bin/option_data +1 -1
- data/bin/place_combo_order +63 -0
- data/bin/place_order +2 -4
- data/bin/template +1 -1
- data/bin/{generic_data.rb → tick_data} +3 -1
- data/bin/time_and_sales +1 -1
- data/lib/ib-ruby.rb +1 -0
- data/lib/ib-ruby/connection.rb +68 -68
- data/lib/ib-ruby/errors.rb +28 -0
- data/lib/ib-ruby/extensions.rb +7 -0
- data/lib/ib-ruby/messages.rb +1 -0
- data/lib/ib-ruby/messages/abstract_message.rb +16 -11
- data/lib/ib-ruby/messages/incoming.rb +125 -329
- data/lib/ib-ruby/messages/incoming/open_order.rb +193 -0
- data/lib/ib-ruby/messages/incoming/ticks.rb +131 -0
- data/lib/ib-ruby/messages/outgoing.rb +44 -45
- data/lib/ib-ruby/models/combo_leg.rb +16 -1
- data/lib/ib-ruby/models/contract.rb +18 -10
- data/lib/ib-ruby/models/contract/bag.rb +1 -7
- data/lib/ib-ruby/models/execution.rb +2 -1
- data/lib/ib-ruby/models/model.rb +1 -1
- data/lib/ib-ruby/models/order.rb +116 -56
- data/lib/ib-ruby/socket.rb +24 -3
- data/spec/account_helper.rb +2 -1
- data/spec/ib-ruby/messages/outgoing_spec.rb +1 -1
- data/spec/ib-ruby/models/combo_leg_spec.rb +0 -1
- data/spec/integration/account_info_spec.rb +2 -2
- data/spec/integration/contract_info_spec.rb +4 -4
- data/spec/integration/depth_data_spec.rb +3 -3
- data/spec/integration/historic_data_spec.rb +1 -1
- data/spec/integration/market_data_spec.rb +4 -4
- data/spec/integration/option_data_spec.rb +1 -1
- data/spec/integration/orders/combo_spec.rb +51 -0
- data/spec/integration/orders/execution_spec.rb +15 -8
- data/spec/integration/orders/placement_spec.rb +46 -72
- data/spec/integration/orders/valid_ids_spec.rb +6 -6
- data/spec/integration_helper.rb +0 -79
- data/spec/order_helper.rb +153 -0
- metadata +13 -4
data/HISTORY
CHANGED
data/README.md
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
# ib-ruby
|
2
2
|
|
3
|
-
Ruby Implementation of the Interactive Brokers Trader Workstation (TWS) API v.965.
|
3
|
+
Ruby Implementation of the Interactive Brokers Trader Workstation (TWS) API v.965-967.
|
4
4
|
|
5
|
-
Copyright (C) 2006-
|
5
|
+
Copyright (C) 2006-2012 Paul Legato, Wes Devauld, and Ar Vicco.
|
6
6
|
|
7
7
|
https://github.com/ib-ruby/ib-ruby
|
8
8
|
|
9
9
|
__WARNING:__ This software is provided __AS-IS__ with __NO WARRANTY__, express or
|
10
|
-
implied. Your use of this software is at your own risk. It may contain
|
11
|
-
|
12
|
-
|
10
|
+
implied. Your use of this software is at your own risk. It may contain any number
|
11
|
+
of bugs, known or unknown, which might cause you to lose money if you use it.
|
12
|
+
You've been warned.
|
13
13
|
|
14
|
-
This code is not sanctioned or supported by Interactive Brokers
|
15
|
-
This software is available under the LGPL. See the file LICENSE for full licensing details.
|
14
|
+
This code is not sanctioned or supported by Interactive Brokers.
|
16
15
|
|
17
16
|
## REQUIREMENTS:
|
18
17
|
|
19
|
-
Either the Interactive Brokers
|
20
|
-
[
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
Either the Interactive Brokers [TWS](http://www.interactivebrokers.com/en/p.php?f=tws) or
|
19
|
+
[Gateway](http://www.interactivebrokers.com/en/p.php?f=programInterface&ib_entity=llc)
|
20
|
+
software must be installed and configured to allow API connections from the computer
|
21
|
+
you plan to run ib-ruby on, which is typically localhost if you're running ib-ruby on
|
22
|
+
the same machine as TWS.
|
23
|
+
|
24
|
+
As a rule of thumb, most recent version of ib-ruby gem only supports latest versions
|
25
|
+
of TWS/Gateway API. Older versions of API are supported by previous gem versions:
|
26
|
+
|
27
|
+
ib-ruby gem TWS version API version
|
28
|
+
0.5.21 918-920 965
|
29
|
+
0.6.1 921-923 966
|
30
|
+
0.7.1 924+ 967
|
25
31
|
|
26
32
|
## INSTALLATION:
|
27
33
|
|
@@ -44,7 +50,7 @@ and :port options given to IB::Connection.new.
|
|
44
50
|
|
45
51
|
require 'ib-ruby'
|
46
52
|
|
47
|
-
ib = IB::Connection.new
|
53
|
+
ib = IB::Connection.new :port => 7496 # TWS on localhost
|
48
54
|
ib.subscribe(:Alert, :AccountValue) { |msg| puts msg.to_human }
|
49
55
|
ib.send_message :RequestAccountData
|
50
56
|
ib.wait_for :AccountDownloadEnd
|
@@ -72,7 +78,7 @@ from TWS, with the received message as its argument.
|
|
72
78
|
|
73
79
|
Then, you request specific data from TWS using `Connection#send_message` or place
|
74
80
|
your order using `Connection#place_order`. TWS will respond with messages that you
|
75
|
-
should have subscribed for, and these messages
|
81
|
+
should have subscribed for, and these messages will be processed in a code block
|
76
82
|
given to `#subscribe`.
|
77
83
|
|
78
84
|
In order to give TWS time to respond, you either run a message processing loop or
|
@@ -88,6 +94,8 @@ directory for more scenarios and examples of handling IB messages.
|
|
88
94
|
|
89
95
|
## LICENSE:
|
90
96
|
|
97
|
+
This software is available under the LGPL.
|
98
|
+
|
91
99
|
This library is free software; you can redistribute it and/or modify
|
92
100
|
it under the terms of the GNU Lesser General Public License as
|
93
101
|
published by the Free Software Foundation; either version 2.1 of the
|
@@ -95,8 +103,8 @@ License, or (at your option) any later version.
|
|
95
103
|
|
96
104
|
This library is distributed in the hope that it will be useful, but
|
97
105
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
98
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
99
|
-
Lesser General Public License
|
106
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE
|
107
|
+
for full licensing details of GNU Lesser General Public License.
|
100
108
|
|
101
109
|
You should have received a copy of the GNU Lesser General Public
|
102
110
|
License along with this library; if not, write to the Free Software
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/bin/account_info
CHANGED
data/bin/cancel_orders
CHANGED
@@ -13,7 +13,7 @@ require 'bundler/setup'
|
|
13
13
|
require 'ib-ruby'
|
14
14
|
|
15
15
|
# First, connect to IB TWS.
|
16
|
-
ib = IB::Connection.new
|
16
|
+
ib = IB::Connection.new :client_id => 1112 # Arbitrary id to identify your script
|
17
17
|
|
18
18
|
# Subscribe to TWS alerts/errors and order-related messages
|
19
19
|
ib.subscribe(:Alert, :OpenOrder, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.to_human }
|
@@ -21,6 +21,7 @@ ib.subscribe(:Alert, :OpenOrder, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.t
|
|
21
21
|
if ARGV.empty?
|
22
22
|
ib.send_message :RequestGlobalCancel
|
23
23
|
else
|
24
|
+
# Will only work for Orders placed under the same :client_id
|
24
25
|
ib.cancel_order *ARGV
|
25
26
|
end
|
26
27
|
|
data/bin/contract_details
CHANGED
@@ -18,7 +18,7 @@ require 'ib-ruby'
|
|
18
18
|
144 => IB::Symbols::Stocks[:wrong]}
|
19
19
|
|
20
20
|
# Connect to IB TWS.
|
21
|
-
ib = IB::Connection.new
|
21
|
+
ib = IB::Connection.new :client_id => 1112 # Arbitrary id to identify your script
|
22
22
|
|
23
23
|
# Subscribe to TWS alerts/errors
|
24
24
|
ib.subscribe(IB::Messages::Incoming::Alert) { |msg| puts msg.to_human }
|
@@ -34,4 +34,5 @@ ib.subscribe(:ContractData) { |msg| puts msg.contract.inspect }
|
|
34
34
|
ib.send_message :RequestContractData, :id => id, :contract => contract
|
35
35
|
end
|
36
36
|
|
37
|
-
|
37
|
+
# Wait for IB to respond to our request
|
38
|
+
ib.wait_for :ContractDataEnd
|
data/bin/depth_of_market
CHANGED
data/bin/historic_data
CHANGED
data/bin/historic_data_cli
CHANGED
@@ -1,11 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
3
2
|
# This script connects to IB API, and downloads historic data
|
4
|
-
# TODO: Fix the Historical command line client
|
5
3
|
|
6
4
|
require 'rubygems'
|
7
5
|
require 'time'
|
8
|
-
require 'duration'
|
9
6
|
require 'pathname'
|
10
7
|
require 'getopt/long'
|
11
8
|
require 'bundler/setup'
|
@@ -20,22 +17,19 @@ include Getopt
|
|
20
17
|
opt = Getopt::Long.getopts(
|
21
18
|
["--help", BOOLEAN],
|
22
19
|
["--end", REQUIRED],
|
20
|
+
["--port", REQUIRED],
|
23
21
|
["--security", REQUIRED],
|
24
22
|
["--duration", REQUIRED],
|
25
23
|
["--barsize", REQUIRED],
|
26
|
-
["--
|
24
|
+
["--csv", BOOLEAN],
|
27
25
|
["--dateformat", REQUIRED],
|
28
|
-
["--nonregularhours", BOOLEAN]
|
26
|
+
["--nonregularhours", BOOLEAN]
|
29
27
|
)
|
30
28
|
|
31
29
|
if opt["help"] || opt["security"].nil? || opt["security"].empty?
|
32
30
|
puts <<ENDHELP
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
This program requires a TWS running on localhost on the standard port
|
37
|
-
that uses API protocol version 15 or higher. Any modern TWS should
|
38
|
-
work. (Patches to make it work on an arbitrary host/port are welcome.)
|
32
|
+
This program requires a TWS or Gateway running on localhost.
|
39
33
|
|
40
34
|
----------
|
41
35
|
|
@@ -46,82 +40,65 @@ One argument is required: --security, the security specification you want, in
|
|
46
40
|
|
47
41
|
Fields not needed for a particular security should be left blank (e.g. strike and right are only relevant for options.)
|
48
42
|
|
49
|
-
For example, to query the
|
50
|
-
the correct command line is:
|
43
|
+
For example, to query the Apple 500 Strike Call expiring in January 2013, use:
|
51
44
|
|
52
|
-
|
45
|
+
$ historic_data_cli --security AAPL:OPT:201301:500:CALL::SMART::USD:
|
53
46
|
|
54
|
-
Consult
|
55
|
-
irb and run security#serialize_ib_ruby
|
47
|
+
Consult contract.rb for allowed values, and see also the examples in the symbols/ directory
|
48
|
+
(load them in irb and run security#serialize_ib_ruby to see the appropriate string.)
|
56
49
|
|
57
|
-
|
50
|
+
----------
|
58
51
|
|
59
52
|
Options:
|
60
53
|
|
61
54
|
--end is is the last time we want data for. The default is now.
|
62
55
|
This is eval'ed by Ruby, so you can use a Ruby expression, which must return a Time object.
|
63
56
|
|
57
|
+
--duration is time period before --end, in seconds. The default is 86400 sec (1 day).
|
58
|
+
TWS imposes limit of 86400 sec (1 day) worth of historic data per request.
|
64
59
|
|
65
|
-
--
|
66
|
-
|
67
|
-
The TWS-imposed limit is 86400 (1 day per request.) Requests for more than 86400 seconds worth of historic data will fail.
|
68
|
-
|
69
|
-
--what determines what the data will be comprised of. This can be "trades", "midpoint", "bid", or "asked".
|
70
|
-
The default is "trades".
|
60
|
+
--what determines what the data will be comprised of. This can be
|
61
|
+
"trades", "midpoint", "bid", or "ask". The default is "trades".
|
71
62
|
|
72
63
|
--barsize determines how long each bar will be.
|
73
64
|
|
74
|
-
Possible values (from the IB documentation):
|
75
|
-
|
76
|
-
1 = 1 sec
|
77
|
-
2 = 5 sec
|
78
|
-
3 = 15 sec
|
79
|
-
4 = 30 sec
|
80
|
-
5 = 1 minute
|
81
|
-
6 = 2 minutes
|
82
|
-
7 = 5 minutes
|
83
|
-
8 = 15 minutes
|
84
|
-
9 = 30 minutes
|
85
|
-
10 = 1 hour
|
86
|
-
11 = 1 day
|
65
|
+
Possible bar values (from the IB documentation):
|
66
|
+
Values less than 30 sec do not appear to work for some securities.
|
87
67
|
|
88
|
-
|
89
|
-
|
68
|
+
sec1 = 1 sec
|
69
|
+
sec5 = 5 sec
|
70
|
+
sec15 = 15 sec
|
71
|
+
sec30 = 30 sec
|
72
|
+
min1 = 1 minute
|
73
|
+
min2 = 2 minutes
|
74
|
+
min5 = 5 minutes
|
75
|
+
min15 = 15 minutes (default)
|
76
|
+
min30 = 30 minutes
|
77
|
+
hour1 = 1 hour
|
78
|
+
day1 = 1 day
|
90
79
|
|
91
|
-
--nonregularhours :
|
92
|
-
|
93
|
-
|
94
|
-
span requested is returned, even data bars covering time
|
95
|
-
intervals where the market in question was illiquid. If
|
80
|
+
--nonregularhours : Normally, only data from the instrument's regular trading
|
81
|
+
hours is returned. If --nonregularhours is given, all data available during the time
|
82
|
+
span requested is returned, even for time intervals when the market was illiquid.
|
96
83
|
|
84
|
+
--dateformat : 1 (default) human-readable time/date format, like "20050307 11:32:16".
|
85
|
+
If you set it to 2 instead, you will get UNIX epoch offsets (seconds since Jan 1, 1970).
|
97
86
|
|
98
|
-
--
|
99
|
-
messages with the historic data to be in a text format, like
|
100
|
-
"20050307 11:32:16". If you set it to 2 instead, you
|
101
|
-
will get an offset in seconds from the beginning of 1970, which
|
102
|
-
is the same format as the UNIX epoch time.
|
87
|
+
--csv : print out the historic data in CSV format, with header.
|
103
88
|
|
104
|
-
|
105
|
-
|
106
|
-
--header : if present, prints a 1 line CSV header describing the fields in the CSV.
|
107
|
-
|
108
|
-
Otherwise, in the default mode, prints only the historic data (and any errors),
|
109
|
-
and prints the data in CSV format.
|
89
|
+
--port : 4001 for Gateway (default), 7496 for TWS, or your custom port
|
110
90
|
|
111
91
|
ENDHELP
|
112
|
-
|
113
92
|
exit
|
114
|
-
|
115
93
|
end
|
116
94
|
|
117
95
|
### Parameters
|
118
96
|
|
119
97
|
# DURATION is how much historic data we want, in seconds, before END_DATE_TIME.
|
120
|
-
|
121
|
-
DURATION = (opt["duration"] && opt["duration"].to_i) || 1.day
|
98
|
+
DURATION = (opt["duration"] && opt["duration"].to_i) || 86400
|
122
99
|
|
123
100
|
if DURATION > 86400
|
124
|
-
STDERR.puts("\nTWS
|
101
|
+
STDERR.puts("\nTWS rejects --duration longer than 86400 seconds (1 day).\n")
|
125
102
|
exit(1)
|
126
103
|
end
|
127
104
|
|
@@ -131,22 +108,10 @@ END_DATE_TIME = (opt["end"] && eval(opt["end"]).to_ib) || Time.now.to_ib
|
|
131
108
|
# This can be :trades, :midpoint, :bid, or :asked
|
132
109
|
WHAT = (opt["what"] && opt["what"].to_sym) || :trades
|
133
110
|
|
134
|
-
|
135
|
-
# 1 = 1 sec
|
136
|
-
# 2 = 5 sec
|
137
|
-
# 3 = 15 sec
|
138
|
-
# 4 = 30 sec
|
139
|
-
# 5 = 1 minute
|
140
|
-
# 6 = 2 minutes
|
141
|
-
# 7 = 5 minutes
|
142
|
-
# 8 = 15 minutes
|
143
|
-
# 9 = 30 minutes
|
144
|
-
# 10 = 1 hour
|
145
|
-
# 11 = 1 day
|
146
|
-
#
|
111
|
+
|
147
112
|
# Values less than 4 do not appear to actually work; they are rejected by the server.
|
148
113
|
#
|
149
|
-
BAR_SIZE = (opt["barsize"] && opt["barsize"].
|
114
|
+
BAR_SIZE = (opt["barsize"] && opt["barsize"].to_sym) || :min15
|
150
115
|
|
151
116
|
# If REGULAR_HOURS_ONLY is set to 0, all data available during the time
|
152
117
|
# span requested is returned, even data bars covering time
|
@@ -165,25 +130,16 @@ REGULAR_HOURS_ONLY = opt["nonregularhours"] ? 0 : 1
|
|
165
130
|
|
166
131
|
DATE_FORMAT = (opt["dateformat"] && opt["dateformat"].to_i) || 1
|
167
132
|
|
168
|
-
|
169
|
-
# Definition of what we want market data for. We have to keep track
|
170
|
-
# of what ticker id corresponds to what symbol ourselves, because the
|
171
|
-
# ticks don't include any other identifying information.
|
172
|
-
#
|
173
|
-
# The choice of ticker ids is, as far as I can tell, arbitrary.
|
174
|
-
#
|
175
|
-
# Note that as of 4/07 there is no historical data available for forex spot.
|
176
|
-
#
|
177
|
-
@market = {123 => opt["security"]}
|
133
|
+
PORT = (opt["port"] && opt["port"]) || '4001'
|
178
134
|
|
179
135
|
# First, connect to IB TWS.
|
180
|
-
ib = IB::Connection.new
|
136
|
+
ib = IB::Connection.new :client_id => 1112 # Arbitrary id to identify your script
|
181
137
|
|
182
|
-
|
138
|
+
# Subscribe to TWS alerts/errors
|
139
|
+
ib.subscribe(:Alert) { |msg| puts msg.to_human }
|
183
140
|
|
184
141
|
lastMessageTime = Queue.new # for communicating with the reader thread.
|
185
142
|
|
186
|
-
#
|
187
143
|
# Subscribe to incoming HistoricalData events. The code passed in the
|
188
144
|
# block will be executed when a message of the subscribed type is
|
189
145
|
# received, with the received message as its argument. In this case,
|
@@ -194,15 +150,15 @@ lastMessageTime = Queue.new # for communicating with the reader thread.
|
|
194
150
|
# The incoming message packet from TWS just identifies it by ticker id.
|
195
151
|
#
|
196
152
|
ib.subscribe(:HistoricalData) do |msg|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
puts datum.to_s
|
202
|
-
else
|
203
|
-
puts "#{datum.date},#{datum.open},#{datum.high},#{datum.low}," +
|
153
|
+
if opt["csv"]
|
154
|
+
puts "date,time,open,high,low,close,volume,wap,has_gaps"
|
155
|
+
msg.results.each do |datum|
|
156
|
+
puts "#{datum.time},#{datum.open},#{datum.high},#{datum.low}," +
|
204
157
|
"#{datum.close},#{datum.volume},#{datum.wap},#{datum.has_gaps}"
|
205
158
|
end
|
159
|
+
else
|
160
|
+
STDERR.puts "Received #{msg.count} items:"
|
161
|
+
msg.results.each { |datum| puts datum.to_s }
|
206
162
|
end
|
207
163
|
lastMessageTime.push(Time.now)
|
208
164
|
end
|
@@ -210,18 +166,15 @@ end
|
|
210
166
|
# Now we actually request historical data for the symbols we're
|
211
167
|
# interested in. TWS will respond with a HistoricalData message,
|
212
168
|
# which will be received by the code above.
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
:format_date => DATE_FORMAT
|
223
|
-
|
224
|
-
}
|
169
|
+
ib.send_message :RequestHistoricalData,
|
170
|
+
:request_id => 123,
|
171
|
+
:contract => opt["security"],
|
172
|
+
:end_date_time => END_DATE_TIME,
|
173
|
+
:duration => DURATION, # seconds == 1 hour
|
174
|
+
:bar_size => BAR_SIZE, # 1 minute bars
|
175
|
+
:what_to_show => WHAT,
|
176
|
+
:use_RTH => REGULAR_HOURS_ONLY,
|
177
|
+
:format_date => DATE_FORMAT
|
225
178
|
|
226
179
|
# A complication here is that IB does not send any indication when all historic data is done being delivered.
|
227
180
|
# So we have to guess - when there is no more new data for some period, we interpret that as "end of data" and exit.
|
data/bin/list_orders
CHANGED
@@ -12,12 +12,12 @@ require 'bundler/setup'
|
|
12
12
|
require 'ib-ruby'
|
13
13
|
|
14
14
|
# First, connect to IB TWS.
|
15
|
-
ib = IB::Connection.new :client_id => 0
|
15
|
+
ib = IB::Connection.new :client_id => 0 # All Orders, including TWS-generated ones
|
16
16
|
|
17
17
|
# Subscribe to TWS alerts/errors and order-related messages
|
18
18
|
@counter = 0
|
19
19
|
|
20
|
-
ib.subscribe(:Alert, :OrderStatus) { |msg| puts msg.to_human }
|
20
|
+
ib.subscribe(:Alert, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.to_human }
|
21
21
|
|
22
22
|
ib.subscribe(:OpenOrder) do |msg|
|
23
23
|
@counter += 1
|
@@ -26,4 +26,5 @@ end
|
|
26
26
|
|
27
27
|
ib.send_message :RequestAllOpenOrders
|
28
28
|
|
29
|
-
|
29
|
+
# Wait for IB to respond to our request
|
30
|
+
ib.wait_for :OpenOrderEnd
|
data/bin/market_data
CHANGED
@@ -18,7 +18,7 @@ require 'ib-ruby'
|
|
18
18
|
789 => IB::Symbols::Forex[:usdcad]}
|
19
19
|
|
20
20
|
# First, connect to IB TWS.
|
21
|
-
ib = IB::Connection.new
|
21
|
+
ib = IB::Connection.new :client_id => 1112 # Arbitrary id to identify your script
|
22
22
|
|
23
23
|
## Subscribe to TWS alerts/errors
|
24
24
|
ib.subscribe(:Alert) { |msg| puts msg.to_human }
|