ib-ruby 0.5.7 → 0.5.9
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 +8 -0
- data/VERSION +1 -1
- data/bin/global_cancel +26 -0
- data/bin/place_order +34 -0
- data/lib/ib-ruby/connection.rb +19 -7
- data/lib/ib-ruby/messages/incoming.rb +4 -3
- data/lib/ib-ruby/messages/outgoing.rb +18 -7
- data/lib/ib-ruby/models/combo_leg.rb +1 -1
- data/lib/ib-ruby/models/contract.rb +1 -1
- data/lib/ib-ruby/models/order.rb +9 -1
- metadata +5 -1
data/HISTORY
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.9
|
data/bin/global_cancel
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This script allows you to cancel ALL Orders opened via IB API at once.
|
4
|
+
# Useful if your robot goes crazy and opens gazillions of wrong limit orders.
|
5
|
+
|
6
|
+
require 'pathname'
|
7
|
+
LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
|
8
|
+
$LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'bundler/setup'
|
12
|
+
require 'ib-ruby'
|
13
|
+
|
14
|
+
# First, connect to IB TWS.
|
15
|
+
ib = IB::Connection.new
|
16
|
+
|
17
|
+
# Subscribe to TWS alerts/errors and account-related messages
|
18
|
+
# that TWS sends in response to account data request
|
19
|
+
ib.subscribe(:Alert, :OpenOrder, :OrderStatus) { |msg| puts msg.to_human }
|
20
|
+
|
21
|
+
ib.send_message :RequestGlobalCancel
|
22
|
+
|
23
|
+
ib.send_message :RequestAllOpenOrders
|
24
|
+
|
25
|
+
puts "\n******** Press <Enter> to cancel... *********\n\n"
|
26
|
+
gets
|
data/bin/place_order
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This script connects to IB API, subscribes to account info and prints out
|
4
|
+
# messages received from IB (update every 3 minute or so)
|
5
|
+
|
6
|
+
require 'pathname'
|
7
|
+
LIB_DIR = (Pathname.new(__FILE__).dirname + '../lib/').realpath.to_s
|
8
|
+
$LOAD_PATH.unshift LIB_DIR unless $LOAD_PATH.include?(LIB_DIR)
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'bundler/setup'
|
12
|
+
require 'ib-ruby'
|
13
|
+
|
14
|
+
# First, connect to IB TWS.
|
15
|
+
ib = IB::Connection.new
|
16
|
+
|
17
|
+
# Subscribe to TWS alerts/errors and account-related messages
|
18
|
+
# that TWS sends in response to account data request
|
19
|
+
ib.subscribe(:Alert, :OpenOrder, :OrderStatus) { |msg| puts msg.to_human }
|
20
|
+
|
21
|
+
wfc = IB::Symbols::Stocks[:wfc]
|
22
|
+
buy_order = IB::Models::Order.new :total_quantity => 100,
|
23
|
+
:limit_price => 1 + rand().round(2),
|
24
|
+
:action => 'BUY',
|
25
|
+
:order_type => 'LMT'
|
26
|
+
|
27
|
+
sleep 0.5 # waiting for :NextValidID
|
28
|
+
|
29
|
+
ib.place_order buy_order, wfc
|
30
|
+
|
31
|
+
ib.send_message :RequestAllOpenOrders
|
32
|
+
|
33
|
+
puts "\n******** Press <Enter> to cancel... *********\n\n"
|
34
|
+
gets
|
data/lib/ib-ruby/connection.rb
CHANGED
@@ -22,12 +22,13 @@ module IB
|
|
22
22
|
DEFAULT_OPTIONS = {:host =>"127.0.0.1",
|
23
23
|
:port => '4001', # IB Gateway connection (default)
|
24
24
|
#:port => '7496', # TWS connection, with annoying pop-ups
|
25
|
+
:client_id => nil, # Will be randomly assigned
|
25
26
|
:connect => true,
|
26
27
|
:reader => true
|
27
28
|
}
|
28
29
|
|
29
|
-
attr_reader :server
|
30
|
-
|
30
|
+
attr_reader :server # Info about IB server and server connection state
|
31
|
+
attr_accessor :next_order_id # Next valid order id
|
31
32
|
|
32
33
|
def initialize(opts = {})
|
33
34
|
@options = DEFAULT_OPTIONS.merge(opts)
|
@@ -67,8 +68,10 @@ module IB
|
|
67
68
|
@server[:local_connect_time] = Time.now()
|
68
69
|
@server[:remote_connect_time] = @server[:socket].read_string
|
69
70
|
|
70
|
-
# Sending arbitrary client ID to identify subsequent communications.
|
71
|
-
|
71
|
+
# Sending (arbitrary) client ID to identify subsequent communications.
|
72
|
+
# The client with a client_id of 0 can manage the TWS-owned open orders.
|
73
|
+
# Other clients can only manage their own open orders.
|
74
|
+
@server[:client_id] = @options[:client_id] || random_id
|
72
75
|
@server[:socket].send(@server[:client_id])
|
73
76
|
|
74
77
|
@connected = true
|
@@ -161,9 +164,9 @@ module IB
|
|
161
164
|
msg_id = @server[:socket].read_int
|
162
165
|
|
163
166
|
# Debug:
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
+
unless [1, 2, 4, 6, 7, 8, 9, 12, 21, 53].include? msg_id
|
168
|
+
puts "Got message #{msg_id} (#{Messages::Incoming::Table[msg_id]})"
|
169
|
+
end
|
167
170
|
|
168
171
|
# Create new instance of the appropriate message type, and have it read the message.
|
169
172
|
# NB: Failure here usually means unsupported message type received
|
@@ -173,6 +176,15 @@ module IB
|
|
173
176
|
puts "No subscribers for message #{msg.class}!" if subscribers[msg.class].empty?
|
174
177
|
end
|
175
178
|
|
179
|
+
# Place Order (convenience wrapper for message :PlaceOrder)
|
180
|
+
def place_order order, contract
|
181
|
+
send_message :PlaceOrder,
|
182
|
+
:order => order,
|
183
|
+
:contract => contract,
|
184
|
+
:id => @next_order_id
|
185
|
+
@next_order_id += 1
|
186
|
+
end
|
187
|
+
|
176
188
|
protected
|
177
189
|
|
178
190
|
def random_id
|
@@ -364,7 +364,8 @@ module IB
|
|
364
364
|
|
365
365
|
@order = Models::Order.new :id => @socket.read_int
|
366
366
|
|
367
|
-
@contract = Models::Contract.new :
|
367
|
+
@contract = Models::Contract.new :con_id => @socket.read_string,
|
368
|
+
:symbol => @socket.read_string,
|
368
369
|
:sec_type => @socket.read_string,
|
369
370
|
:expiry => @socket.read_string,
|
370
371
|
:strike => @socket.read_decimal,
|
@@ -397,7 +398,7 @@ module IB
|
|
397
398
|
@order.fa_percentage = @socket.read_string
|
398
399
|
@order.fa_profile = @socket.read_string
|
399
400
|
@order.good_till_date = @socket.read_string
|
400
|
-
@order.
|
401
|
+
@order.rule_80a = @socket.read_string
|
401
402
|
@order.percent_offset = @socket.read_decimal
|
402
403
|
@order.settling_firm = @socket.read_string
|
403
404
|
@order.short_sale_slot = @socket.read_int
|
@@ -431,7 +432,7 @@ module IB
|
|
431
432
|
@order.trail_stop_price = @socket.read_decimal
|
432
433
|
@order.basis_points = @socket.read_decimal
|
433
434
|
@order.basis_points_type = @socket.read_int
|
434
|
-
@
|
435
|
+
@contract.combo_legs_description = @socket.read_string
|
435
436
|
@order.scale_init_level_size = @socket.read_int_max
|
436
437
|
@order.scale_subs_level_size = @socket.read_int_max
|
437
438
|
@order.scale_price_increment = @socket.read_decimal_max
|
@@ -73,35 +73,46 @@ module IB
|
|
73
73
|
|
74
74
|
### Defining (short) Outgoing Message classes for IB:
|
75
75
|
|
76
|
-
|
76
|
+
## Empty messages (no data)
|
77
|
+
|
78
|
+
# Request the open orders that were placed from THIS client. Each open order
|
79
|
+
# will be fed back through the OpenOrder and OrderStatus messages.
|
80
|
+
# NB: Client with a client_id of 0 will also receive the TWS-owned open orders.
|
81
|
+
# These orders will be associated with the client and a new orderId will be
|
82
|
+
# generated. This association will persist over multiple API and TWS sessions.
|
77
83
|
RequestOpenOrders = def_message 5
|
78
|
-
|
84
|
+
|
85
|
+
# Request the open orders placed from all clients and also from TWS. Each open
|
86
|
+
# order will be fed back through the OpenOrder and OrderStatus messages.
|
79
87
|
RequestAllOpenOrders = def_message 16
|
80
|
-
|
88
|
+
|
81
89
|
# Requests an XML document that describes the valid parameters that a scanner
|
82
90
|
# subscription can have (for outgoing RequestScannerSubscription message).
|
83
91
|
RequestScannerParameters = def_message 24
|
92
|
+
|
93
|
+
CancelNewsBulletins = def_message 13
|
94
|
+
RequestManagedAccounts = def_message 17
|
84
95
|
RequestCurrentTime = def_message 49
|
85
96
|
RequestGlobalCancel = def_message 58
|
86
97
|
|
87
|
-
|
98
|
+
## Data format is: @data = { :id => ticker_id}
|
88
99
|
CancelMarketData = def_message 2
|
89
100
|
CancelMarketDepth = def_message 11
|
90
101
|
CancelScannerSubscription = def_message 23
|
91
102
|
CancelHistoricalData = def_message 25
|
92
103
|
CancelRealTimeBars = def_message 51
|
93
104
|
|
94
|
-
|
105
|
+
## Data format is: @data = { :id => request_id }
|
95
106
|
CancelFundamentalData = def_message 53
|
96
107
|
CancelImpliedVolatility = def_message 56
|
97
108
|
CancelCalculateImpliedVolatility = CancelImpliedVolatility
|
98
109
|
CancelOptionPrice = def_message 57
|
99
110
|
CancelCalculateOptionPrice = CancelOptionPrice
|
100
111
|
|
101
|
-
|
112
|
+
## Data format is: @data ={ :id => order-id-to-cancel }
|
102
113
|
CancelOrder = def_message 4
|
103
114
|
|
104
|
-
|
115
|
+
## These messages contain just one or two keys, shown in the end of definition
|
105
116
|
# @data = { :number_of_ids => int }
|
106
117
|
RequestIds = def_message 8, 1, :number_of_ids
|
107
118
|
# data = { :all_messages => boolean }
|
@@ -141,7 +141,7 @@ module IB
|
|
141
141
|
def right=(x)
|
142
142
|
x.upcase! if x.is_a?(String)
|
143
143
|
x = nil if !x.nil? && x.empty?
|
144
|
-
x = nil if x == "0"
|
144
|
+
x = nil if x == "0" || x == "?"
|
145
145
|
raise(ArgumentError.new("Invalid right \"#{x}\" (must be one of PUT, CALL, P, C)")) unless x.nil? || ["PUT", "CALL", "P", "C", "0"].include?(x)
|
146
146
|
@right = x
|
147
147
|
end
|
data/lib/ib-ruby/models/order.rb
CHANGED
@@ -261,7 +261,7 @@ module IB
|
|
261
261
|
# This returns an Array of data from the given order,
|
262
262
|
# mixed with data from associated contract. Ugly mix, indeed.
|
263
263
|
def serialize_with contract
|
264
|
-
[contract.serialize_long(:sec_id),
|
264
|
+
[contract.serialize_long(:con_id, :sec_id),
|
265
265
|
action, # main order fields
|
266
266
|
total_quantity,
|
267
267
|
order_type,
|
@@ -336,6 +336,14 @@ module IB
|
|
336
336
|
end
|
337
337
|
end
|
338
338
|
|
339
|
+
def to_s #human
|
340
|
+
"<Order::" + #self.class.
|
341
|
+
instance_variables.map do |key|
|
342
|
+
value = instance_variable_get(key)
|
343
|
+
" #{key}=#{value}" unless value.nil? || value == '' || value == 0
|
344
|
+
end.compact.join(',') + " >"
|
345
|
+
end
|
346
|
+
|
339
347
|
end # class Order
|
340
348
|
end # module Models
|
341
349
|
end # module IB
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: ib-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.5.
|
5
|
+
version: 0.5.9
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paul Legato
|
@@ -44,10 +44,12 @@ executables:
|
|
44
44
|
- account_info
|
45
45
|
- contract_details
|
46
46
|
- depth_of_market
|
47
|
+
- global_cancel
|
47
48
|
- historic_data
|
48
49
|
- historic_data_cli
|
49
50
|
- market_data
|
50
51
|
- option_data
|
52
|
+
- place_order
|
51
53
|
- template
|
52
54
|
- time_and_sales
|
53
55
|
extensions: []
|
@@ -58,10 +60,12 @@ files:
|
|
58
60
|
- bin/account_info
|
59
61
|
- bin/contract_details
|
60
62
|
- bin/depth_of_market
|
63
|
+
- bin/global_cancel
|
61
64
|
- bin/historic_data
|
62
65
|
- bin/historic_data_cli
|
63
66
|
- bin/market_data
|
64
67
|
- bin/option_data
|
68
|
+
- bin/place_order
|
65
69
|
- bin/template
|
66
70
|
- bin/time_and_sales
|
67
71
|
- lib/ib-ruby/connection.rb
|