ib-ruby 0.5.7 → 0.5.9

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -73,3 +73,11 @@
73
73
  == 0.5.7 / 2011-12-16
74
74
 
75
75
  * Option subclass of Contract added
76
+
77
+ == 0.5.8 / 2011-12-16
78
+
79
+ * Critical bugs in Order-related messages fixed
80
+
81
+ == 0.5.9 / 2011-12-16
82
+
83
+ * Sample scripts to place and cancel orders added
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.7
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
@@ -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, # Info about IB server and server connection state
30
- :next_order_id # Next valid order id
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
- @server[:client_id] = random_id
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
- #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
+ 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 :symbol => @socket.read_string,
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.rule_80A = @socket.read_string
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
- @order.combo_legs_description = @socket.read_string
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
- # Empty messages (no data)
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
- CancelNewsBulletins = def_message 13
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
- RequestManagedAccounts = def_message 17
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
- # Data format is: @data = { :id => ticker_id}
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
- # Data format is: @data = { :id => request_id }
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
- # Data format is: @data ={ :id => order-id-to-cancel }
112
+ ## Data format is: @data ={ :id => order-id-to-cancel }
102
113
  CancelOrder = def_message 4
103
114
 
104
- # These messages contain just one or two keys, shown in the end of definition
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 }
@@ -34,7 +34,7 @@ module IB
34
34
  def initialize opts = {}
35
35
  @con_id = 0
36
36
  @ratio = 0
37
- @open_close = 0
37
+ @open_close = SAME
38
38
  @short_sale_slot = 0
39
39
  @designated_location = ''
40
40
  @exempt_code = -1
@@ -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
@@ -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.7
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