ib-ruby 0.5.2 → 0.5.7
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/.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.
|