ib-ruby 0.5.2 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/HISTORY +16 -0
- data/README.md +83 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/account_info +0 -0
- data/bin/contract_details +0 -0
- data/bin/depth_of_market +0 -0
- data/bin/historic_data +0 -0
- data/bin/historic_data_cli +0 -0
- data/bin/market_data +0 -0
- data/bin/option_data +5 -3
- data/bin/template +0 -0
- data/bin/time_and_sales +0 -0
- data/lib/ib-ruby/connection.rb +8 -6
- data/lib/ib-ruby/constants.rb +17 -0
- data/lib/ib-ruby/models/combo_leg.rb +58 -0
- data/lib/ib-ruby/models/contract/option.rb +54 -0
- data/lib/ib-ruby/models/contract.rb +13 -64
- data/lib/ib-ruby/models/order.rb +1 -1
- data/lib/ib-ruby/models.rb +1 -0
- data/lib/ib-ruby/symbols/futures.rb +36 -1
- data/lib/ib-ruby/symbols/options.rb +21 -15
- data/lib/{version.rb → ib-ruby/version.rb} +1 -1
- data/lib/ib-ruby.rb +6 -2
- data/spec/ib-ruby/models/combo_leg_spec.rb +55 -0
- data/spec/ib-ruby/models/contract_spec.rb +0 -54
- metadata +19 -19
- data/README.rdoc +0 -58
data/.gitignore
CHANGED
data/HISTORY
CHANGED
@@ -57,3 +57,19 @@
|
|
57
57
|
== 0.5.2 / 2011-10-30
|
58
58
|
|
59
59
|
* Add nonblocking Connection#process_messages API
|
60
|
+
|
61
|
+
== 0.5.4 / 2011-12-16
|
62
|
+
|
63
|
+
* Placing Version source file into its proper place
|
64
|
+
|
65
|
+
== 0.5.5 / 2011-12-16
|
66
|
+
|
67
|
+
* ComboLeg serialization signature changed
|
68
|
+
|
69
|
+
== 0.5.6 / 2011-12-16
|
70
|
+
|
71
|
+
* Models::ComboLeg given a source file of its own
|
72
|
+
|
73
|
+
== 0.5.7 / 2011-12-16
|
74
|
+
|
75
|
+
* Option subclass of Contract added
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# ib-ruby
|
2
|
+
|
3
|
+
Ruby Implementation of the Interactive Brokers Trader Workstation (TWS) API v.965.
|
4
|
+
|
5
|
+
Copyright (C) 2006-2011 Paul Legato, Wes Devauld, and Ar Vicco.
|
6
|
+
|
7
|
+
https://github.com/ib-ruby/ib-ruby
|
8
|
+
|
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
|
+
any number of bugs, known or unknown, which might cause you to lose
|
12
|
+
money if you use it. You've been warned.
|
13
|
+
|
14
|
+
__It is specifically NOT RECOMMENDED that this code be used for live trading.__
|
15
|
+
|
16
|
+
This code is not sanctioned or supported by Interactive Brokers
|
17
|
+
This software is available under the LGPL. See the file LICENSE for full licensing details.
|
18
|
+
|
19
|
+
|
20
|
+
## REQUIREMENTS:
|
21
|
+
|
22
|
+
Either the Interactive Brokers
|
23
|
+
[TWS](http://www.interactivebrokers.com/en/p.php?f=tws) or
|
24
|
+
[IB Gateway](http://www.interactivebrokers.com/en/control/systemstandalone-ibGateway.php?os=unix&ib_entity=llc)
|
25
|
+
software must be installed and configured to allow API connections
|
26
|
+
from the computer you plan to run ib-ruby on, which is typically
|
27
|
+
localhost if you're running ib-ruby on the same machine as TWS.
|
28
|
+
|
29
|
+
## INSTALLATION:
|
30
|
+
|
31
|
+
### From RubyGems
|
32
|
+
|
33
|
+
$ sudo gem install ib-ruby
|
34
|
+
|
35
|
+
### From Source
|
36
|
+
|
37
|
+
$ git clone https://github.com/ib-ruby/ib-ruby
|
38
|
+
$ cd ib-ruby; rake gem:install
|
39
|
+
|
40
|
+
## SYNOPSIS:
|
41
|
+
|
42
|
+
First, start up Interactive Broker's Trader Work Station or Gateway.
|
43
|
+
Make sure it is configured to allow API connections on localhost.
|
44
|
+
|
45
|
+
>> require 'ib-ruby'
|
46
|
+
>> ib = IB::Connection.new
|
47
|
+
>> ib.subscribe(:Alert, :AccountValue) { |msg| puts msg.to_human }
|
48
|
+
>> ib.send_message :RequestAccountData, :subscribe => true
|
49
|
+
|
50
|
+
Your code and TWS interact via an exchange of messages. You
|
51
|
+
subscribe to message types you're interested in using
|
52
|
+
`IB::Connection#subscribe` and request data from TWS using
|
53
|
+
`IB::Connection#send_message`.
|
54
|
+
|
55
|
+
The code blocks (or procs) given to `#subscribe` will be executed when
|
56
|
+
a message of the requested type is received, with the received message as
|
57
|
+
its argument.
|
58
|
+
|
59
|
+
See `lib/ib-ruby/messages` for a full list of supported incoming/outgoing messages and
|
60
|
+
their attributes. The original TWS docs and code samples can be found
|
61
|
+
in the `misc/` folder.
|
62
|
+
|
63
|
+
The sample scripts in the `bin/` directory provide examples of how
|
64
|
+
common tasks can be achieved using ib-ruby.
|
65
|
+
|
66
|
+
|
67
|
+
## LICENSE:
|
68
|
+
|
69
|
+
This library is free software; you can redistribute it and/or modify
|
70
|
+
it under the terms of the GNU Lesser General Public License as
|
71
|
+
published by the Free Software Foundation; either version 2.1 of the
|
72
|
+
License, or (at your option) any later version.
|
73
|
+
|
74
|
+
This library is distributed in the hope that it will be useful, but
|
75
|
+
WITHOUT ANY WARRANTY; without even the implied warranty of
|
76
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
77
|
+
Lesser General Public License for more details.
|
78
|
+
|
79
|
+
You should have received a copy of the GNU Lesser General Public
|
80
|
+
License along with this library; if not, write to the Free Software
|
81
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
82
|
+
02110-1301 USA
|
83
|
+
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.7
|
data/bin/account_info
CHANGED
File without changes
|
data/bin/contract_details
CHANGED
File without changes
|
data/bin/depth_of_market
CHANGED
File without changes
|
data/bin/historic_data
CHANGED
File without changes
|
data/bin/historic_data_cli
CHANGED
File without changes
|
data/bin/market_data
CHANGED
File without changes
|
data/bin/option_data
CHANGED
@@ -13,8 +13,10 @@ require 'ib-ruby'
|
|
13
13
|
# Definition of what we want market data for. We have to keep track of what ticker id
|
14
14
|
# corresponds to what symbol ourselves, because the ticks don't include any other
|
15
15
|
# identifying information. The choice of ticker ids is, as far as I can tell, arbitrary.
|
16
|
-
@market = {
|
17
|
-
|
16
|
+
@market = {13 => IB::Symbols::Options[:wfc20],
|
17
|
+
15 => IB::Symbols::Options[:z50],
|
18
|
+
17 => IB::Symbols::Options[:spy75],
|
19
|
+
19 => IB::Symbols::Options[:spy100]}
|
18
20
|
|
19
21
|
# First, connect to IB TWS.
|
20
22
|
ib = IB::Connection.new
|
@@ -28,7 +30,7 @@ ib.subscribe(:Alert) { |msg| puts msg.to_human }
|
|
28
30
|
#
|
29
31
|
# (N.B. The description field is not from IB TWS. It is defined
|
30
32
|
# locally in forex.rb, and is just arbitrary text.)
|
31
|
-
ib.subscribe(:TickPrice, :TickSize, :TickOption) do |msg|
|
33
|
+
ib.subscribe(:TickPrice, :TickSize, :TickOption, :TickString) do |msg|
|
32
34
|
puts @market[msg.data[:id]].description + ": " + msg.to_human
|
33
35
|
end
|
34
36
|
|
data/bin/template
CHANGED
File without changes
|
data/bin/time_and_sales
CHANGED
File without changes
|
data/lib/ib-ruby/connection.rb
CHANGED
@@ -146,7 +146,7 @@ module IB
|
|
146
146
|
|
147
147
|
alias dispatch send_message # Legacy alias
|
148
148
|
|
149
|
-
# Process incoming messages during *poll_time* (200) msecs
|
149
|
+
# Process incoming messages during *poll_time* (200) msecs
|
150
150
|
def process_messages poll_time = 200 # in msec
|
151
151
|
time_out = Time.now + poll_time/1000.0
|
152
152
|
while (time_left = time_out - Time.now) > 0
|
@@ -161,9 +161,9 @@ module IB
|
|
161
161
|
msg_id = @server[:socket].read_int
|
162
162
|
|
163
163
|
# Debug:
|
164
|
-
unless [1, 2, 4, 6, 7, 8, 9, 12, 21, 53].include? msg_id
|
165
|
-
|
166
|
-
end
|
164
|
+
#unless [1, 2, 4, 6, 7, 8, 9, 12, 21, 53].include? msg_id
|
165
|
+
# puts "Got message #{msg_id} (#{Messages::Incoming::Table[msg_id]})"
|
166
|
+
#end
|
167
167
|
|
168
168
|
# Create new instance of the appropriate message type, and have it read the message.
|
169
169
|
# NB: Failure here usually means unsupported message type received
|
@@ -185,10 +185,12 @@ module IB
|
|
185
185
|
def start_reader
|
186
186
|
Thread.abort_on_exception = true
|
187
187
|
@reader_running = true
|
188
|
-
@server[:reader] = Thread.new
|
188
|
+
@server[:reader] = Thread.new do
|
189
|
+
process_messages while @reader_running
|
190
|
+
end
|
189
191
|
end
|
190
192
|
|
191
193
|
end # class Connection
|
192
|
-
IB = Connection # Legacy alias
|
194
|
+
#IB = Connection # Legacy alias
|
193
195
|
|
194
196
|
end # module IB
|
data/lib/ib-ruby/constants.rb
CHANGED
@@ -97,4 +97,21 @@ module IB
|
|
97
97
|
# this new tick type are: 0 = Not halted, 1 = Halted.
|
98
98
|
# Note 3: Applies to bond contracts only.
|
99
99
|
}
|
100
|
+
|
101
|
+
#
|
102
|
+
# Market depth messages contain these "operation" codes to tell you
|
103
|
+
# what to do with the data.
|
104
|
+
#
|
105
|
+
# See also http://www.interactivebrokers.com/php/apiUsersGuide/apiguide/java/updatemktdepth.htm
|
106
|
+
#
|
107
|
+
MARKET_DEPTH_OPERATIONS = {
|
108
|
+
0 => :insert, # New order, insert into the row identified by :position
|
109
|
+
1 => :update, # Update the existing order at the row identified by :position
|
110
|
+
2 => :delete # Delete the existing order at the row identified by :position
|
111
|
+
}
|
112
|
+
|
113
|
+
MARKET_DEPTH_SIDES = {
|
114
|
+
0 => :ask,
|
115
|
+
1 => :bid
|
116
|
+
}
|
100
117
|
end # module IB
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module IB
|
2
|
+
module Models
|
3
|
+
|
4
|
+
# ComboLeg objects represent individual securities in a "BAG" contract - which
|
5
|
+
# is not really a contract, but a combination (combo) of securities. AKA basket
|
6
|
+
# or bag of securities.
|
7
|
+
class ComboLeg < Model
|
8
|
+
# // open/close leg value is same as combo
|
9
|
+
# Specifies whether the order is an open or close order. Valid values are:
|
10
|
+
SAME = 0 # Same as the parent security. The only option for retail customers.
|
11
|
+
OPEN = 1 # Open. This value is only valid for institutional customers.
|
12
|
+
CLOSE = 2 # Close. This value is only valid for institutional customers.
|
13
|
+
UNKNOWN = 3
|
14
|
+
|
15
|
+
attr_accessor :con_id, # int: The unique contract identifier specifying the security.
|
16
|
+
:ratio, # int: Select the relative number of contracts for the leg you
|
17
|
+
# are constructing. To help determine the ratio for a
|
18
|
+
# specific combination order, refer to the Interactive
|
19
|
+
# Analytics section of the User's Guide.
|
20
|
+
|
21
|
+
:action, # String: BUY/SELL/SSHORT/SSHORTX
|
22
|
+
# The side (buy or sell) for the leg you are constructing.
|
23
|
+
:exchange, # String: exchange to which the complete combination
|
24
|
+
# order will be routed.
|
25
|
+
:open_close, # int: Specifies whether the order is an open or close order.
|
26
|
+
# Valid values: ComboLeg::SAME/OPEN/CLOSE/UNKNOWN
|
27
|
+
|
28
|
+
# For institutional customers only! For stock legs when doing short sale
|
29
|
+
:short_sale_slot, # int: 0 - retail, 1 = clearing broker, 2 = third party
|
30
|
+
:designated_location, # String: Only for shortSaleSlot == 2.
|
31
|
+
# Otherwise leave blank or orders will be rejected.
|
32
|
+
:exempt_code # int: ?
|
33
|
+
|
34
|
+
def initialize opts = {}
|
35
|
+
@con_id = 0
|
36
|
+
@ratio = 0
|
37
|
+
@open_close = 0
|
38
|
+
@short_sale_slot = 0
|
39
|
+
@designated_location = ''
|
40
|
+
@exempt_code = -1
|
41
|
+
|
42
|
+
super opts
|
43
|
+
end
|
44
|
+
|
45
|
+
# Some messages include open_close, some don't. wtf.
|
46
|
+
def serialize *fields
|
47
|
+
[con_id,
|
48
|
+
ratio,
|
49
|
+
action,
|
50
|
+
exchange,
|
51
|
+
(fields.include?(:extended) ? [open_close, short_sale_slot,
|
52
|
+
designated_location, exempt_code] : [])
|
53
|
+
].flatten
|
54
|
+
end
|
55
|
+
end # ComboLeg
|
56
|
+
|
57
|
+
end # module Models
|
58
|
+
end # module IB
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ib-ruby/models/contract'
|
2
|
+
|
3
|
+
module IB
|
4
|
+
module Models
|
5
|
+
class Contract
|
6
|
+
class Option < Contract
|
7
|
+
|
8
|
+
# For Options, this is contract's OSI (Option Symbology Initiative) name/code
|
9
|
+
alias osi local_symbol
|
10
|
+
|
11
|
+
def osi= value
|
12
|
+
# Normalize to 21 char
|
13
|
+
self.local_symbol = value.sub(/ /, ' '*(22-value.size))
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize opts = {}
|
17
|
+
super opts
|
18
|
+
@sec_type = IB::SECURITY_TYPES[:option]
|
19
|
+
@description ||= osi ? osi : "#{symbol} #{strike} #{right} #{expiry}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Make valid IB Contract definition from OSI (Option Symbology Initiative) code.
|
23
|
+
# NB: Simply making a new Contract with *local_symbol* (osi) property set to a
|
24
|
+
# valid OSI code works just as well, just do NOT set *expiry*, *right* or
|
25
|
+
# *strike* properties at the same time.
|
26
|
+
# This class method provided as a backup, to show how to analyse OSI codes.
|
27
|
+
def self.from_osi osi
|
28
|
+
|
29
|
+
# Parse contract's OSI (OCC Option Symbology Initiative) code
|
30
|
+
args = osi.match(/(\w+)\s?(\d\d)(\d\d)(\d\d)([pcPC])(\d+)/).to_a.drop(1)
|
31
|
+
symbol = args.shift
|
32
|
+
year = 2000 + args.shift.to_i
|
33
|
+
month = args.shift.to_i
|
34
|
+
day = args.shift.to_i
|
35
|
+
right = args.shift.upcase
|
36
|
+
strike = args.shift.to_i/1000.0
|
37
|
+
#p symbol, year, month, day, right, strike
|
38
|
+
|
39
|
+
# Set correct expiry date - IB expiry date differs from OSI if expiry date
|
40
|
+
# falls on Saturday (see https://github.com/arvicco/option_mower/issues/4)
|
41
|
+
expiry_date = Time.new(year, month, day)
|
42
|
+
expiry_date = Time.new(year, month, day-1) if expiry_date.saturday?
|
43
|
+
|
44
|
+
new :symbol => symbol,
|
45
|
+
:exchange => "SMART",
|
46
|
+
:expiry => expiry_date.to_ib,
|
47
|
+
:right => right,
|
48
|
+
:strike => strike
|
49
|
+
end
|
50
|
+
|
51
|
+
end # class Option
|
52
|
+
end # class Contract
|
53
|
+
end # module Models
|
54
|
+
end # module IB
|
@@ -168,7 +168,7 @@ module IB
|
|
168
168
|
# This returns an Array of data from the given contract.
|
169
169
|
# Different messages serialize contracts differently. Go figure.
|
170
170
|
# Note that it does NOT include the combo legs.
|
171
|
-
def serialize
|
171
|
+
def serialize *fields
|
172
172
|
[(fields.include?(:con_id) ? [con_id] : []),
|
173
173
|
symbol,
|
174
174
|
sec_type,
|
@@ -182,12 +182,12 @@ module IB
|
|
182
182
|
].flatten
|
183
183
|
end
|
184
184
|
|
185
|
-
def serialize_long
|
186
|
-
serialize
|
185
|
+
def serialize_long *fields
|
186
|
+
serialize :option, :primary_exchange, *fields
|
187
187
|
end
|
188
188
|
|
189
|
-
def serialize_short
|
190
|
-
serialize
|
189
|
+
def serialize_short *fields
|
190
|
+
serialize :option, *fields
|
191
191
|
end
|
192
192
|
|
193
193
|
# This produces a string uniquely identifying this contract, in the format used
|
@@ -202,12 +202,12 @@ module IB
|
|
202
202
|
# expiring in September, 2008, the string is:
|
203
203
|
#
|
204
204
|
# GBP:FUT:200809:::62500:GLOBEX::USD:
|
205
|
-
def serialize_ib_ruby
|
205
|
+
def serialize_ib_ruby version
|
206
206
|
serialize.join(":")
|
207
207
|
end
|
208
208
|
|
209
209
|
# This returns a Contract initialized from the serialize_ib_ruby format string.
|
210
|
-
def self.from_ib_ruby
|
210
|
+
def self.from_ib_ruby string
|
211
211
|
c = Contract.new
|
212
212
|
c.symbol, c.sec_type, c.expiry, c.strike, c.right, c.multiplier,
|
213
213
|
c.exchange, c.primary_exchange, c.currency, c.local_symbol = string.split(":")
|
@@ -215,7 +215,7 @@ module IB
|
|
215
215
|
end
|
216
216
|
|
217
217
|
# Serialize under_comp parameters
|
218
|
-
def serialize_under_comp
|
218
|
+
def serialize_under_comp *args
|
219
219
|
# EClientSocket.java, line 471:
|
220
220
|
if under_comp
|
221
221
|
[true,
|
@@ -228,12 +228,14 @@ module IB
|
|
228
228
|
end
|
229
229
|
|
230
230
|
# Some messages send open_close too, some don't. WTF.
|
231
|
-
|
232
|
-
|
231
|
+
# "BAG" is not really a contract, but a combination (combo) of securities.
|
232
|
+
# AKA basket or bag of securities. Individual securities in combo are represented
|
233
|
+
# by ComboLeg objects.
|
234
|
+
def serialize_combo_legs *fields
|
233
235
|
return [] unless sec_type.upcase == "BAG"
|
234
236
|
return [0] if combo_legs.empty? || combo_legs.nil?
|
235
237
|
[combo_legs.size,
|
236
|
-
combo_legs.map { |leg| leg.serialize
|
238
|
+
combo_legs.map { |leg| leg.serialize *fields }]
|
237
239
|
end
|
238
240
|
|
239
241
|
def to_human
|
@@ -248,59 +250,6 @@ module IB
|
|
248
250
|
to_human
|
249
251
|
end
|
250
252
|
|
251
|
-
# ComboLeg is an internal class of Contract, as it should be
|
252
|
-
class ComboLeg < Model
|
253
|
-
# // open/close leg value is same as combo
|
254
|
-
# Specifies whether the order is an open or close order. Valid values are:
|
255
|
-
SAME = 0 # Same as the parent security. The only option for retail customers.
|
256
|
-
OPEN = 1 # Open. This value is only valid for institutional customers.
|
257
|
-
CLOSE = 2 # Close. This value is only valid for institutional customers.
|
258
|
-
UNKNOWN = 3
|
259
|
-
|
260
|
-
|
261
|
-
attr_accessor :con_id, # int: The unique contract identifier specifying the security.
|
262
|
-
:ratio, # int: Select the relative number of contracts for the leg you
|
263
|
-
# are constructing. To help determine the ratio for a
|
264
|
-
# specific combination order, refer to the Interactive
|
265
|
-
# Analytics section of the User's Guide.
|
266
|
-
|
267
|
-
:action, # String: BUY/SELL/SSHORT/SSHORTX
|
268
|
-
# The side (buy or sell) for the leg you are constructing.
|
269
|
-
:exchange, # String: exchange to which the complete combination
|
270
|
-
# order will be routed.
|
271
|
-
:open_close, # int: Specifies whether the order is an open or close order.
|
272
|
-
# Valid values: ComboLeg::SAME/OPEN/CLOSE/UNKNOWN
|
273
|
-
|
274
|
-
# For institutional customers only! For stock legs when doing short sale
|
275
|
-
:short_sale_slot, # int: 0 - retail, 1 = clearing broker, 2 = third party
|
276
|
-
:designated_location, # String: Only for shortSaleSlot == 2.
|
277
|
-
# Otherwise leave blank or orders will be rejected.
|
278
|
-
:exempt_code # int: ?
|
279
|
-
|
280
|
-
def initialize opts = {}
|
281
|
-
@con_id = 0
|
282
|
-
@ratio = 0
|
283
|
-
@open_close = 0
|
284
|
-
@short_sale_slot = 0
|
285
|
-
@designated_location = ''
|
286
|
-
@exempt_code = -1
|
287
|
-
|
288
|
-
super opts
|
289
|
-
end
|
290
|
-
|
291
|
-
# Some messages include open_close, some don't. wtf.
|
292
|
-
def serialize(type = :short)
|
293
|
-
[con_id,
|
294
|
-
ratio,
|
295
|
-
action,
|
296
|
-
exchange] +
|
297
|
-
type == :short ? [] : [open_close,
|
298
|
-
short_sale_slot,
|
299
|
-
designated_location,
|
300
|
-
exempt_code]
|
301
|
-
end
|
302
|
-
end # ComboLeg
|
303
|
-
|
304
253
|
end # class Contract
|
305
254
|
end # module Models
|
306
255
|
end # module IB
|
data/lib/ib-ruby/models/order.rb
CHANGED
@@ -281,7 +281,7 @@ module IB
|
|
281
281
|
trigger_method,
|
282
282
|
outside_rth, # was: ignore_rth
|
283
283
|
hidden,
|
284
|
-
contract.serialize_combo_legs(:
|
284
|
+
contract.serialize_combo_legs(:extended),
|
285
285
|
'', # deprecated shares_allocation field
|
286
286
|
discretionary_amount,
|
287
287
|
good_after_time,
|
data/lib/ib-ruby/models.rb
CHANGED
@@ -17,7 +17,6 @@ module IB
|
|
17
17
|
#
|
18
18
|
# N.B. This will not work as expected near/after expiration during that month, as
|
19
19
|
# volume rolls over to the next quarter even though the current month is still valid!
|
20
|
-
#
|
21
20
|
def self.next_quarter_month(time)
|
22
21
|
sprintf("%02d", [3, 6, 9, 12].find { |month| month >= time.month })
|
23
22
|
end
|
@@ -30,10 +29,46 @@ module IB
|
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
32
|
+
#
|
33
|
+
# WARNING: This is currently broken. It returns the next
|
34
|
+
# quarterly expiration month after the current month. Many futures
|
35
|
+
# instruments have monthly contracts for the near months. This
|
36
|
+
# method will not work for such contracts; it will return the next
|
37
|
+
# quarter after the current month, even though the present month
|
38
|
+
# has the majority of the trading volume.
|
39
|
+
#
|
40
|
+
# For example, in early November of 2011, many contracts have the
|
41
|
+
# vast majority of their volume in the Nov 2011 contract, but this
|
42
|
+
# method will return the Dec 2011 contract instead.
|
43
|
+
#
|
33
44
|
def self.next_expiry(time)
|
34
45
|
"#{ self.next_quarter_year(time) }#{ self.next_quarter_month(time) }"
|
35
46
|
end
|
36
47
|
|
48
|
+
#
|
49
|
+
# Convenience method; generates a Models::Contract instance for a futures
|
50
|
+
# contract with the given parameters.
|
51
|
+
#
|
52
|
+
# If expiry is nil, it will use the end month of the current
|
53
|
+
# quarter. This will be wrong for most contracts most of the time,
|
54
|
+
# since most contracts have the majority of their volume in a
|
55
|
+
# nearby intraquarter month.
|
56
|
+
#
|
57
|
+
# It is recommended that you specify an expiration date manually
|
58
|
+
# until next_expiry is fixed. Expiry should be a string in the
|
59
|
+
# format "YYYYMM", where YYYY is the 4 digit year and MM is the 2
|
60
|
+
# digit month. For example, November 2011 is "201111".
|
61
|
+
#
|
62
|
+
def self.future(base_symbol, exchange, currency, description="", expiry=nil)
|
63
|
+
Models::Contract.new(:symbol => base_symbol,
|
64
|
+
:expiry => expiry.nil? ? self.next_expiry(Time.now) : expiry,
|
65
|
+
:exchange => exchange,
|
66
|
+
:currency => currency,
|
67
|
+
:sec_type => SECURITY_TYPES[:future],
|
68
|
+
:description => description)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
37
72
|
Futures ={
|
38
73
|
:ym => Models::Contract.new(:symbol => "YM",
|
39
74
|
:expiry => self.next_expiry(Time.now),
|
@@ -8,21 +8,27 @@ module IB
|
|
8
8
|
module Symbols
|
9
9
|
|
10
10
|
Options =
|
11
|
-
{:wfc20 => Models::Contract.new(:symbol => "WFC",
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
{:wfc20 => Models::Contract::Option.new(:symbol => "WFC",
|
12
|
+
:exchange => "SMART",
|
13
|
+
:expiry => "201207",
|
14
|
+
:right => "CALL",
|
15
|
+
:strike => 20.0,
|
16
|
+
:description => "Wells Fargo 20 Call 2012-07"),
|
17
|
+
:z50 => Models::Contract::Option.new(:symbol => "Z",
|
18
|
+
:exchange => "LIFFE",
|
19
|
+
:expiry => "201206",
|
20
|
+
:right => "CALL",
|
21
|
+
:strike => 50.0,
|
22
|
+
:description => " FTSE-100 index 50 Call 2012-06"),
|
23
|
+
:spy75 => Models::Contract::Option.new(:symbol => 'SPY',
|
24
|
+
:exchange => "SMART",
|
25
|
+
:expiry => "20120615",
|
26
|
+
:right => "P",
|
27
|
+
:currency => "USD",
|
28
|
+
:strike => 75.0,
|
29
|
+
:description => "SPY 75.0 Put 2012-06-16"),
|
30
|
+
:spy100 => Models::Contract::Option.new(:osi => 'SPY 121222P00100000',
|
31
|
+
:exchange => "SMART"),
|
26
32
|
}
|
27
33
|
end
|
28
34
|
end
|
data/lib/ib-ruby.rb
CHANGED
@@ -2,9 +2,13 @@ module IB
|
|
2
2
|
end # module IB
|
3
3
|
IbRuby = IB
|
4
4
|
|
5
|
-
require 'version'
|
5
|
+
require 'ib-ruby/version'
|
6
6
|
require 'ib-ruby/constants'
|
7
7
|
require 'ib-ruby/connection'
|
8
8
|
require 'ib-ruby/models'
|
9
|
-
require 'ib-ruby/symbols'
|
10
9
|
require 'ib-ruby/messages'
|
10
|
+
|
11
|
+
# TODO Where should we require this?
|
12
|
+
require 'ib-ruby/models/contract/option'
|
13
|
+
|
14
|
+
require 'ib-ruby/symbols'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
|
2
|
+
|
3
|
+
describe IB::Models::ComboLeg do
|
4
|
+
|
5
|
+
let(:properties) do
|
6
|
+
{:con_id => 123,
|
7
|
+
:ratio=> 1234,
|
8
|
+
:action => 'BUY',
|
9
|
+
:exchange => 'BLAH',
|
10
|
+
:open_close => IB::Models::ComboLeg::OPEN,
|
11
|
+
:short_sale_slot => 1,
|
12
|
+
:designated_location => 'BLEH',
|
13
|
+
:exempt_code => 12}
|
14
|
+
end
|
15
|
+
|
16
|
+
context "instantiation" do
|
17
|
+
context 'empty without properties' do
|
18
|
+
subject { IB::Models::ComboLeg.new }
|
19
|
+
|
20
|
+
it { should_not be_nil }
|
21
|
+
its (:con_id) {should == 0}
|
22
|
+
its (:ratio) {should == 0}
|
23
|
+
its (:open_close) {should == 0}
|
24
|
+
its (:short_sale_slot) {should == 0}
|
25
|
+
its (:exempt_code) {should == -1}
|
26
|
+
|
27
|
+
its (:created_at) {should be_a Time}
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with properties' do
|
31
|
+
subject { IB::Models::ComboLeg.new properties }
|
32
|
+
|
33
|
+
it 'sets properties right' do
|
34
|
+
properties.each do |name, value|
|
35
|
+
subject.send(name).should == value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'essential properties are still set, even if not given explicitely' do
|
40
|
+
its (:created_at) {should be_a Time}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'allows setting attributes' do
|
45
|
+
expect {
|
46
|
+
x = IB::Models::ComboLeg.new
|
47
|
+
properties.each do |name, value|
|
48
|
+
subject.send("#{name}=", value)
|
49
|
+
subject.send(name).should == value
|
50
|
+
end
|
51
|
+
}.to_not raise_error
|
52
|
+
end
|
53
|
+
end #instantiation
|
54
|
+
|
55
|
+
end # describe IB::Models::Contract::ComboLeg
|
@@ -205,57 +205,3 @@ describe IB::Models::Contract do
|
|
205
205
|
end #serialization
|
206
206
|
|
207
207
|
end # describe IB::Models::Contract
|
208
|
-
|
209
|
-
describe IB::Models::Contract::ComboLeg do
|
210
|
-
|
211
|
-
let(:properties) do
|
212
|
-
{:con_id => 123,
|
213
|
-
:ratio=> 1234,
|
214
|
-
:action => 'BUY',
|
215
|
-
:exchange => 'BLAH',
|
216
|
-
:open_close => IB::Models::Contract::ComboLeg::OPEN,
|
217
|
-
:short_sale_slot => 1,
|
218
|
-
:designated_location => 'BLEH',
|
219
|
-
:exempt_code => 12}
|
220
|
-
end
|
221
|
-
|
222
|
-
context "instantiation" do
|
223
|
-
context 'empty without properties' do
|
224
|
-
subject { IB::Models::Contract::ComboLeg.new }
|
225
|
-
|
226
|
-
it { should_not be_nil }
|
227
|
-
its (:con_id) {should == 0}
|
228
|
-
its (:ratio) {should == 0}
|
229
|
-
its (:open_close) {should == 0}
|
230
|
-
its (:short_sale_slot) {should == 0}
|
231
|
-
its (:exempt_code) {should == -1}
|
232
|
-
|
233
|
-
its (:created_at) {should be_a Time}
|
234
|
-
end
|
235
|
-
|
236
|
-
context 'with properties' do
|
237
|
-
subject { IB::Models::Contract::ComboLeg.new properties }
|
238
|
-
|
239
|
-
it 'sets properties right' do
|
240
|
-
properties.each do |name, value|
|
241
|
-
subject.send(name).should == value
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
context 'essential properties are still set, even if not given explicitely' do
|
246
|
-
its (:created_at) {should be_a Time}
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
it 'allows setting attributes' do
|
251
|
-
expect {
|
252
|
-
x = IB::Models::Contract::ComboLeg.new
|
253
|
-
properties.each do |name, value|
|
254
|
-
subject.send("#{name}=", value)
|
255
|
-
subject.send(name).should == value
|
256
|
-
end
|
257
|
-
}.to_not raise_error
|
258
|
-
end
|
259
|
-
end #instantiation
|
260
|
-
|
261
|
-
end # describe IB::Models::Contract::ComboLeg
|
metadata
CHANGED
@@ -2,15 +2,16 @@
|
|
2
2
|
name: ib-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.5.
|
5
|
+
version: 0.5.7
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
+
- Paul Legato
|
8
9
|
- arvicco
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
13
|
|
13
|
-
date: 2011-12-
|
14
|
+
date: 2011-12-16 00:00:00 -08:00
|
14
15
|
default_executable:
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
@@ -35,8 +36,10 @@ dependencies:
|
|
35
36
|
version: 2.5.0
|
36
37
|
type: :development
|
37
38
|
version_requirements: *id002
|
38
|
-
description: Ruby Implementation of the Interactive
|
39
|
-
email:
|
39
|
+
description: Ruby Implementation of the Interactive Brokers TWS API
|
40
|
+
email:
|
41
|
+
- pjlegato@gmail.com
|
42
|
+
- arvitallian@gmail.com
|
40
43
|
executables:
|
41
44
|
- account_info
|
42
45
|
- contract_details
|
@@ -49,10 +52,8 @@ executables:
|
|
49
52
|
- time_and_sales
|
50
53
|
extensions: []
|
51
54
|
|
52
|
-
extra_rdoc_files:
|
53
|
-
|
54
|
-
- HISTORY
|
55
|
-
- README.rdoc
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
56
57
|
files:
|
57
58
|
- bin/account_info
|
58
59
|
- bin/contract_details
|
@@ -69,6 +70,8 @@ files:
|
|
69
70
|
- lib/ib-ruby/messages/outgoing.rb
|
70
71
|
- lib/ib-ruby/messages.rb
|
71
72
|
- lib/ib-ruby/models/bar.rb
|
73
|
+
- lib/ib-ruby/models/combo_leg.rb
|
74
|
+
- lib/ib-ruby/models/contract/option.rb
|
72
75
|
- lib/ib-ruby/models/contract.rb
|
73
76
|
- lib/ib-ruby/models/execution.rb
|
74
77
|
- lib/ib-ruby/models/model.rb
|
@@ -80,9 +83,10 @@ files:
|
|
80
83
|
- lib/ib-ruby/symbols/options.rb
|
81
84
|
- lib/ib-ruby/symbols/stocks.rb
|
82
85
|
- lib/ib-ruby/symbols.rb
|
86
|
+
- lib/ib-ruby/version.rb
|
83
87
|
- lib/ib-ruby.rb
|
84
|
-
- lib/version.rb
|
85
88
|
- spec/ib-ruby/connection_spec.rb
|
89
|
+
- spec/ib-ruby/models/combo_leg_spec.rb
|
86
90
|
- spec/ib-ruby/models/contract_spec.rb
|
87
91
|
- spec/ib-ruby/models/order_spec.rb
|
88
92
|
- spec/ib-ruby_spec.rb
|
@@ -94,23 +98,18 @@ files:
|
|
94
98
|
- tasks/spec.rake
|
95
99
|
- tasks/version.rake
|
96
100
|
- Rakefile
|
97
|
-
- README.
|
101
|
+
- README.md
|
98
102
|
- LICENSE
|
99
103
|
- VERSION
|
100
104
|
- HISTORY
|
101
105
|
- .gitignore
|
102
106
|
has_rdoc: true
|
103
|
-
homepage:
|
107
|
+
homepage: https://github.com/pjlegato/ib-ruby
|
104
108
|
licenses: []
|
105
109
|
|
106
110
|
post_install_message:
|
107
|
-
rdoc_options:
|
108
|
-
|
109
|
-
- UTF-8
|
110
|
-
- --main
|
111
|
-
- README.rdoc
|
112
|
-
- --title
|
113
|
-
- mix
|
111
|
+
rdoc_options: []
|
112
|
+
|
114
113
|
require_paths:
|
115
114
|
- lib
|
116
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -131,9 +130,10 @@ rubyforge_project:
|
|
131
130
|
rubygems_version: 1.6.2
|
132
131
|
signing_key:
|
133
132
|
specification_version: 3
|
134
|
-
summary: Ruby Implementation of the Interactive
|
133
|
+
summary: Ruby Implementation of the Interactive Brokers TWS API
|
135
134
|
test_files:
|
136
135
|
- spec/ib-ruby/connection_spec.rb
|
136
|
+
- spec/ib-ruby/models/combo_leg_spec.rb
|
137
137
|
- spec/ib-ruby/models/contract_spec.rb
|
138
138
|
- spec/ib-ruby/models/order_spec.rb
|
139
139
|
- spec/ib-ruby_spec.rb
|
data/README.rdoc
DELETED
@@ -1,58 +0,0 @@
|
|
1
|
-
= ib-ruby
|
2
|
-
by:: Arvicco
|
3
|
-
url:: http://github.com/arvicco/ib-ruby
|
4
|
-
|
5
|
-
This is a fork of http://github.com/wdevauld/ib-ruby by Wes Devauld,
|
6
|
-
that is in turn forked from http://github.com/pjlegato/ib-ruby by Paul Legato.
|
7
|
-
|
8
|
-
== DESCRIPTION:
|
9
|
-
|
10
|
-
Ruby Implementation of the Interactive Broker' Trader Work Station (TWS) API v.965.
|
11
|
-
|
12
|
-
== FEATURES/PROBLEMS:
|
13
|
-
|
14
|
-
* This is a BETA release, and should not be used for live trading.
|
15
|
-
Any features contained within are AS-IS and may not work in all conditions
|
16
|
-
* This code is not sanctioned or supported by Interactive Brokers
|
17
|
-
|
18
|
-
== REQUIREMENTS:
|
19
|
-
|
20
|
-
Interactive Broker's TWS or Gateway installed and configured to allow API
|
21
|
-
connections on localhost.
|
22
|
-
|
23
|
-
== INSTALL:
|
24
|
-
|
25
|
-
=== From Gem
|
26
|
-
|
27
|
-
$ sudo gem install ib-ruby
|
28
|
-
|
29
|
-
=== From Source
|
30
|
-
|
31
|
-
$ git clone http://github.com/arvicco/ib-ruby
|
32
|
-
$ cd ib-ruby; rake gem:install
|
33
|
-
|
34
|
-
== SYNOPSIS:
|
35
|
-
|
36
|
-
First, start up Interactive Broker's Trader Work Station or Gateway.
|
37
|
-
Make sure it is configured to allow API connections on localhost.
|
38
|
-
|
39
|
-
>> require 'ib-ruby'
|
40
|
-
>> ib = IB::Connection.new
|
41
|
-
>> ib.subscribe(:Alert, :AccountValue) { |msg| puts msg.to_human }
|
42
|
-
>> ib.send_message :RequestAccountData, :subscribe => true
|
43
|
-
|
44
|
-
Essentially, all interaction of your code and TWS can be described as exchange
|
45
|
-
of messages. You subscribe to message type(s) you're interested in using
|
46
|
-
IB::Connection#subscribe and request data from TWS using IB::Connection#send_message.
|
47
|
-
The code blocks (or procs) given to #subscribe will be executed when a message of
|
48
|
-
requested type is received, with the received message as its argument.
|
49
|
-
|
50
|
-
Use sample scripts in /bin folder as an example of how common tasks can be
|
51
|
-
achieved using ib-ruby.
|
52
|
-
|
53
|
-
See /lib/ib-ruby/messages for a full list of TWS incoming/outgoing messages and
|
54
|
-
their attributes. Original TWS docs and code samples can be found in /misc folder.
|
55
|
-
|
56
|
-
== LICENSE:
|
57
|
-
|
58
|
-
Copyright (c) 2011 Arvicco. See LICENSE for details.
|