ib-ruby 0.7.6 → 0.7.8
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +8 -0
- data/Rakefile +8 -0
- data/VERSION +1 -1
- data/bin/fundamental_data +6 -9
- data/lib/ib-ruby/connection.rb +16 -19
- data/lib/ib-ruby/constants.rb +3 -1
- data/lib/ib-ruby/extensions.rb +5 -0
- data/lib/ib-ruby/messages/incoming/contract_data.rb +46 -45
- data/lib/ib-ruby/messages/incoming/delta_neutral_validation.rb +8 -5
- data/lib/ib-ruby/messages/incoming/execution_data.rb +2 -2
- data/lib/ib-ruby/messages/incoming/next_valid_id.rb +18 -0
- data/lib/ib-ruby/messages/incoming/open_order.rb +23 -16
- data/lib/ib-ruby/messages/incoming/order_status.rb +5 -3
- data/lib/ib-ruby/messages/incoming/scanner_data.rb +15 -11
- data/lib/ib-ruby/messages/incoming.rb +1 -5
- data/lib/ib-ruby/messages/outgoing/abstract_message.rb +2 -1
- data/lib/ib-ruby/messages/outgoing/place_order.rb +1 -1
- data/lib/ib-ruby/messages/outgoing.rb +1 -1
- data/lib/ib-ruby/models/bag.rb +59 -0
- data/lib/ib-ruby/models/combo_leg.rb +10 -6
- data/lib/ib-ruby/models/contract.rb +278 -0
- data/lib/ib-ruby/models/contract_detail.rb +70 -0
- data/lib/ib-ruby/models/execution.rb +22 -16
- data/lib/ib-ruby/models/model.rb +75 -17
- data/lib/ib-ruby/models/model_properties.rb +40 -26
- data/lib/ib-ruby/models/option.rb +62 -0
- data/lib/ib-ruby/models/order.rb +122 -86
- data/lib/ib-ruby/models/order_state.rb +11 -12
- data/lib/ib-ruby/models/underlying.rb +36 -0
- data/lib/ib-ruby/models.rb +1 -4
- data/spec/account_helper.rb +2 -1
- data/spec/db.rb +1 -1
- data/spec/db_helper.rb +105 -0
- data/spec/ib-ruby/connection_spec.rb +3 -3
- data/spec/ib-ruby/messages/incoming/open_order_spec.rb +5 -5
- data/spec/ib-ruby/messages/incoming/order_status_spec.rb +3 -3
- data/spec/ib-ruby/models/bag_spec.rb +15 -23
- data/spec/ib-ruby/models/bar_spec.rb +0 -5
- data/spec/ib-ruby/models/combo_leg_spec.rb +18 -25
- data/spec/ib-ruby/models/contract_detail_spec.rb +54 -0
- data/spec/ib-ruby/models/contract_spec.rb +25 -37
- data/spec/ib-ruby/models/execution_spec.rb +64 -19
- data/spec/ib-ruby/models/option_spec.rb +12 -34
- data/spec/ib-ruby/models/order_spec.rb +107 -45
- data/spec/ib-ruby/models/order_state_spec.rb +12 -12
- data/spec/ib-ruby/models/underlying_spec.rb +36 -0
- data/spec/integration/contract_info_spec.rb +65 -55
- data/spec/integration/fundamental_data_spec.rb +2 -2
- data/spec/integration/orders/attached_spec.rb +3 -3
- data/spec/integration/orders/combo_spec.rb +3 -3
- data/spec/integration/orders/placement_spec.rb +8 -8
- data/spec/integration/orders/{execution_spec.rb → trades_spec.rb} +8 -12
- data/spec/integration/orders/valid_ids_spec.rb +3 -3
- data/spec/message_helper.rb +1 -1
- data/spec/model_helper.rb +150 -85
- data/spec/order_helper.rb +35 -18
- metadata +18 -10
- data/lib/ib-ruby/models/contracts/bag.rb +0 -62
- data/lib/ib-ruby/models/contracts/contract.rb +0 -320
- data/lib/ib-ruby/models/contracts/option.rb +0 -66
- data/lib/ib-ruby/models/contracts.rb +0 -27
data/HISTORY
CHANGED
data/Rakefile
CHANGED
@@ -38,3 +38,11 @@ begin
|
|
38
38
|
rescue LoadError => e
|
39
39
|
puts "gem install standalone_migrations to get db:migrate:* tasks! (Error: #{e})"
|
40
40
|
end
|
41
|
+
|
42
|
+
# rake db:redo DB=test"
|
43
|
+
namespace :db do
|
44
|
+
desc "Remake db from scratch: $ rake db:redo DB=test"
|
45
|
+
task :redo => [:drop, :create, :migrate] do
|
46
|
+
puts "Redo Finished!"
|
47
|
+
end
|
48
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.8
|
data/bin/fundamental_data
CHANGED
@@ -16,13 +16,8 @@ ib = IB::Connection.new :client_id => 1112 #, :port => 7496 # TWS
|
|
16
16
|
|
17
17
|
ib.subscribe(:Alert) { |msg| puts msg.to_human }
|
18
18
|
|
19
|
-
# Fundamental Data will arrive as XML
|
20
|
-
ib.subscribe(:FundamentalData)
|
21
|
-
puts 'Got fundamental data.'
|
22
|
-
@xml = XmlSimple.xml_in(msg.data)
|
23
|
-
pp @xml
|
24
|
-
@parsing_finished = true
|
25
|
-
end
|
19
|
+
# Fundamental Data will arrive as XML, parse it
|
20
|
+
ib.subscribe(:FundamentalData) { |msg| @xml = XmlSimple.xml_in(msg.data) }
|
26
21
|
|
27
22
|
ibm = IB::Contract.new :symbol => 'IBM',
|
28
23
|
:exchange => 'NYSE',
|
@@ -38,5 +33,7 @@ ib.send_message :RequestFundamentalData,
|
|
38
33
|
:contract => ibm,
|
39
34
|
:report_type => 'snapshot'
|
40
35
|
|
41
|
-
# Needs some time to receive
|
42
|
-
ib.wait_for(
|
36
|
+
# Needs some time to receive and parse XML. Standard timeout of 1 sec is just too low.
|
37
|
+
ib.wait_for(30) { @xml}
|
38
|
+
|
39
|
+
pp @xml
|
data/lib/ib-ruby/connection.rb
CHANGED
@@ -28,7 +28,10 @@ module IB
|
|
28
28
|
|
29
29
|
attr_accessor :server, # Info about IB server and server connection state
|
30
30
|
:options, # Connection options
|
31
|
-
:
|
31
|
+
:next_local_id # Next valid order id
|
32
|
+
|
33
|
+
alias next_order_id next_local_id
|
34
|
+
alias next_order_id= next_local_id=
|
32
35
|
|
33
36
|
def initialize opts = {}
|
34
37
|
@options = DEFAULT_OPTIONS.merge(opts)
|
@@ -39,7 +42,7 @@ module IB
|
|
39
42
|
|
40
43
|
self.default_logger = options[:logger] if options[:logger]
|
41
44
|
@connected = false
|
42
|
-
|
45
|
+
self.next_local_id = nil
|
43
46
|
@server = Hash.new
|
44
47
|
|
45
48
|
connect if options[:connect]
|
@@ -53,8 +56,8 @@ module IB
|
|
53
56
|
|
54
57
|
# TWS always sends NextValidId message at connect - save this id
|
55
58
|
self.subscribe(:NextValidId) do |msg|
|
56
|
-
|
57
|
-
log.info "Got next valid order id: #{
|
59
|
+
self.next_local_id = msg.local_id
|
60
|
+
log.info "Got next valid order id: #{next_local_id}."
|
58
61
|
end
|
59
62
|
|
60
63
|
server[:socket] = IBSocket.open(options[:host], options[:port])
|
@@ -181,7 +184,9 @@ module IB
|
|
181
184
|
|
182
185
|
# Check if messages of given type were received at_least n times
|
183
186
|
def received? message_type, times=1
|
184
|
-
|
187
|
+
@receive_lock.synchronize do
|
188
|
+
received[message_type].size >= times
|
189
|
+
end
|
185
190
|
end
|
186
191
|
|
187
192
|
# Check if all given conditions are satisfied
|
@@ -292,26 +297,18 @@ module IB
|
|
292
297
|
# Place Order (convenience wrapper for send_message :PlaceOrder).
|
293
298
|
# Assigns client_id and order_id fields to placed order. Returns assigned order_id.
|
294
299
|
def place_order order, contract
|
295
|
-
|
296
|
-
order.client_id = server[:client_id]
|
297
|
-
order.order_id = @next_order_id
|
298
|
-
@next_order_id += 1
|
299
|
-
modify_order order, contract
|
300
|
+
order.place contract, self
|
300
301
|
end
|
301
302
|
|
302
303
|
# Modify Order (convenience wrapper for send_message :PlaceOrder). Returns order_id.
|
303
304
|
def modify_order order, contract
|
304
|
-
|
305
|
-
:order => order,
|
306
|
-
:contract => contract,
|
307
|
-
:order_id => order.order_id
|
308
|
-
order.order_id
|
305
|
+
order.modify contract, self
|
309
306
|
end
|
310
307
|
|
311
|
-
# Cancel Orders by their ids (convenience wrapper for send_message :CancelOrder).
|
312
|
-
def cancel_order *
|
313
|
-
|
314
|
-
send_message :CancelOrder, :
|
308
|
+
# Cancel Orders by their local ids (convenience wrapper for send_message :CancelOrder).
|
309
|
+
def cancel_order *local_ids
|
310
|
+
local_ids.each do |local_id|
|
311
|
+
send_message :CancelOrder, :local_id => local_id.to_i
|
315
312
|
end
|
316
313
|
end
|
317
314
|
|
data/lib/ib-ruby/constants.rb
CHANGED
@@ -171,6 +171,8 @@ module IB
|
|
171
171
|
'FOP' => :futures_option,
|
172
172
|
'CASH' => :forex,
|
173
173
|
'BOND' => :bond,
|
174
|
+
'WAR' => :warrant,
|
175
|
+
'FUND' => :fund, # ETF?
|
174
176
|
'BAG' => :bag}.freeze
|
175
177
|
|
176
178
|
# Obtain symbolic value from given property code:
|
@@ -285,7 +287,7 @@ module IB
|
|
285
287
|
|
286
288
|
# Obtain property code from given symbolic value:
|
287
289
|
# CODES[:side][:buy] -> 'B'
|
288
|
-
CODES = Hash[VALUES.map { |property, hash| [property, hash.invert] }]
|
290
|
+
CODES = Hash[VALUES.map { |property, hash| [property, hash.invert] }].freeze
|
289
291
|
|
290
292
|
# Most common property processors
|
291
293
|
PROPS = {:side =>
|
data/lib/ib-ruby/extensions.rb
CHANGED
@@ -14,68 +14,69 @@ module IB
|
|
14
14
|
[:contract, :currency, :string],
|
15
15
|
[:contract, :local_symbol, :string],
|
16
16
|
|
17
|
-
[:
|
18
|
-
[:
|
17
|
+
[:contract_detail, :market_name, :string], # extended
|
18
|
+
[:contract_detail, :trading_class, :string],
|
19
19
|
[:contract, :con_id, :int],
|
20
|
-
[:
|
20
|
+
[:contract_detail, :min_tick, :decimal],
|
21
21
|
[:contract, :multiplier, :string],
|
22
|
-
[:
|
23
|
-
[:
|
24
|
-
[:
|
25
|
-
[:
|
26
|
-
[:
|
22
|
+
[:contract_detail, :order_types, :string],
|
23
|
+
[:contract_detail, :valid_exchanges, :string],
|
24
|
+
[:contract_detail, :price_magnifier, :int],
|
25
|
+
[:contract_detail, :under_con_id, :int],
|
26
|
+
[:contract_detail, :long_name, :string],
|
27
27
|
[:contract, :primary_exchange, :string],
|
28
|
-
[:
|
29
|
-
[:
|
30
|
-
[:
|
31
|
-
[:
|
32
|
-
[:
|
33
|
-
[:
|
34
|
-
[:
|
28
|
+
[:contract_detail, :contract_month, :string],
|
29
|
+
[:contract_detail, :industry, :string],
|
30
|
+
[:contract_detail, :category, :string],
|
31
|
+
[:contract_detail, :subcategory, :string],
|
32
|
+
[:contract_detail, :time_zone, :string],
|
33
|
+
[:contract_detail, :trading_hours, :string],
|
34
|
+
[:contract_detail, :liquid_hours, :string])
|
35
35
|
|
36
36
|
class ContractData
|
37
37
|
|
38
38
|
def contract
|
39
|
-
@contract = IB::Contract.build @data[:contract]
|
39
|
+
@contract = IB::Contract.build @data[:contract].
|
40
|
+
merge(:contract_detail => contract_detail)
|
40
41
|
end
|
42
|
+
|
43
|
+
def contract_detail
|
44
|
+
@contract_detail = IB::ContractDetail.new @data[:contract_detail]
|
45
|
+
end
|
46
|
+
|
47
|
+
alias contract_details contract_detail
|
48
|
+
|
41
49
|
end # ContractData
|
42
50
|
|
43
51
|
BondContractData =
|
44
|
-
def_message [18, 4],
|
52
|
+
def_message [18, 4], ContractData,
|
45
53
|
[:request_id, :int],
|
46
54
|
[:contract, :symbol, :string],
|
47
55
|
[:contract, :sec_type, :string],
|
48
|
-
[:
|
49
|
-
[:
|
50
|
-
[:
|
51
|
-
[:
|
52
|
-
[:
|
53
|
-
[:
|
54
|
-
[:
|
55
|
-
[:
|
56
|
-
[:
|
57
|
-
[:
|
58
|
-
[:
|
56
|
+
[:contract_detail, :cusip, :string],
|
57
|
+
[:contract_detail, :coupon, :decimal],
|
58
|
+
[:contract_detail, :maturity, :string],
|
59
|
+
[:contract_detail, :issue_date, :string],
|
60
|
+
[:contract_detail, :ratings, :string],
|
61
|
+
[:contract_detail, :bond_type, :string],
|
62
|
+
[:contract_detail, :coupon_type, :string],
|
63
|
+
[:contract_detail, :convertible, :boolean],
|
64
|
+
[:contract_detail, :callable, :boolean],
|
65
|
+
[:contract_detail, :puttable, :boolean],
|
66
|
+
[:contract_detail, :desc_append, :string],
|
59
67
|
[:contract, :exchange, :string],
|
60
68
|
[:contract, :currency, :string],
|
61
|
-
[:
|
62
|
-
[:
|
69
|
+
[:contract_detail, :market_name, :string], # extended
|
70
|
+
[:contract_detail, :trading_class, :string],
|
63
71
|
[:contract, :con_id, :int],
|
64
|
-
[:
|
65
|
-
[:
|
66
|
-
[:
|
67
|
-
[:
|
68
|
-
[:
|
69
|
-
[:
|
70
|
-
[:
|
71
|
-
[:
|
72
|
-
|
73
|
-
class BondContractData
|
74
|
-
|
75
|
-
def contract
|
76
|
-
@contract = IB::Contract.build @data[:contract]
|
77
|
-
end
|
78
|
-
end # BondContractData
|
72
|
+
[:contract_detail, :min_tick, :decimal],
|
73
|
+
[:contract_detail, :order_types, :string],
|
74
|
+
[:contract_detail, :valid_exchanges, :string],
|
75
|
+
[:contract_detail, :valid_next_option_date, :string],
|
76
|
+
[:contract_detail, :valid_next_option_type, :string],
|
77
|
+
[:contract_detail, :valid_next_option_partial, :string],
|
78
|
+
[:contract_detail, :notes, :string],
|
79
|
+
[:contract_detail, :long_name, :string]
|
79
80
|
|
80
81
|
end # module Incoming
|
81
82
|
end # module Messages
|
@@ -6,13 +6,16 @@ module IB
|
|
6
6
|
# - see API Reference p. 26
|
7
7
|
DeltaNeutralValidation = def_message 56,
|
8
8
|
[:request_id, :int],
|
9
|
-
[:
|
10
|
-
[:
|
11
|
-
[:
|
9
|
+
[:underlying, :con_id, :int],
|
10
|
+
[:underlying, :delta, :decimal],
|
11
|
+
[:underlying, :price, :decimal]
|
12
12
|
class DeltaNeutralValidation
|
13
|
-
def
|
14
|
-
@
|
13
|
+
def underlying
|
14
|
+
@underlying = IB::Underlying.new @data[:underlying]
|
15
15
|
end
|
16
|
+
|
17
|
+
alias under_comp underlying
|
18
|
+
|
16
19
|
end # DeltaNeutralValidation
|
17
20
|
|
18
21
|
end # module Incoming
|
@@ -6,7 +6,7 @@ module IB
|
|
6
6
|
def_message [11, 8],
|
7
7
|
# The reqID that was specified previously in the call to reqExecution()
|
8
8
|
[:request_id, :int],
|
9
|
-
[:execution, :
|
9
|
+
[:execution, :local_id, :int],
|
10
10
|
[:contract, :con_id, :int],
|
11
11
|
[:contract, :symbol, :string],
|
12
12
|
[:contract, :sec_type, :string],
|
@@ -22,7 +22,7 @@ module IB
|
|
22
22
|
[:execution, :account_name, :string],
|
23
23
|
[:execution, :exchange, :string],
|
24
24
|
[:execution, :side, :string],
|
25
|
-
[:execution, :
|
25
|
+
[:execution, :quantity, :int],
|
26
26
|
[:execution, :price, :decimal],
|
27
27
|
[:execution, :perm_id, :int],
|
28
28
|
[:execution, :client_id, :int],
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module IB
|
2
|
+
module Messages
|
3
|
+
module Incoming
|
4
|
+
|
5
|
+
# This message is always sent by TWS automatically at connect.
|
6
|
+
# The IB::Connection class subscribes to it automatically and stores
|
7
|
+
# the order id in its @next_local_id attribute.
|
8
|
+
NextValidID = NextValidId = def_message(9, [:local_id, :int])
|
9
|
+
|
10
|
+
class NextValidId
|
11
|
+
|
12
|
+
# Legacy accessor
|
13
|
+
alias order_id local_id
|
14
|
+
|
15
|
+
end # class NextValidId
|
16
|
+
end # module Incoming
|
17
|
+
end # module Messages
|
18
|
+
end # module IB
|
@@ -5,7 +5,7 @@ module IB
|
|
5
5
|
# OpenOrder is the longest message with complex processing logics
|
6
6
|
OpenOrder =
|
7
7
|
def_message [5, [23, 28]],
|
8
|
-
[:order, :
|
8
|
+
[:order, :local_id, :int],
|
9
9
|
|
10
10
|
[:contract, :con_id, :int],
|
11
11
|
[:contract, :symbol, :string],
|
@@ -70,12 +70,20 @@ module IB
|
|
70
70
|
[:order, :delta_neutral_order_type, :string],
|
71
71
|
[:order, :delta_neutral_aux_price, :decimal_max]
|
72
72
|
|
73
|
-
|
74
73
|
class OpenOrder
|
75
74
|
|
76
|
-
|
77
75
|
# Accessors to make OpenOrder API-compatible with OrderStatus message
|
78
76
|
|
77
|
+
def local_id
|
78
|
+
order.local_id
|
79
|
+
end
|
80
|
+
|
81
|
+
alias order_id local_id
|
82
|
+
|
83
|
+
def status
|
84
|
+
order.status
|
85
|
+
end
|
86
|
+
|
79
87
|
def order
|
80
88
|
@order ||= IB::Order.new @data[:order].merge(:order_state => order_state)
|
81
89
|
end
|
@@ -83,23 +91,23 @@ module IB
|
|
83
91
|
def order_state
|
84
92
|
@order_state ||= IB::OrderState.new(
|
85
93
|
@data[:order_state].merge(
|
86
|
-
:
|
94
|
+
:local_id => @data[:order][:local_id],
|
87
95
|
:perm_id => @data[:order][:perm_id],
|
88
96
|
:parent_id => @data[:order][:parent_id],
|
89
97
|
:client_id => @data[:order][:client_id]))
|
90
98
|
end
|
91
99
|
|
92
100
|
def contract
|
93
|
-
@contract ||= IB::Contract.build
|
101
|
+
@contract ||= IB::Contract.build(
|
102
|
+
@data[:contract].merge(:underlying => underlying)
|
103
|
+
)
|
94
104
|
end
|
95
105
|
|
96
|
-
def
|
97
|
-
|
106
|
+
def underlying
|
107
|
+
@underlying = @data[:underlying_present] ? IB::Underlying.new(@data[:underlying]) : nil
|
98
108
|
end
|
99
109
|
|
100
|
-
|
101
|
-
order.status
|
102
|
-
end
|
110
|
+
alias under_comp underlying
|
103
111
|
|
104
112
|
def load
|
105
113
|
super
|
@@ -152,7 +160,6 @@ module IB
|
|
152
160
|
[:order, :scale_profit_offset, :decimal_max],
|
153
161
|
[:order, :scale_auto_reset, :boolean],
|
154
162
|
[:order, :scale_init_position, :int_max],
|
155
|
-
[:order, :scale_init_position, :int_max],
|
156
163
|
[:order, :scale_init_fill_qty, :decimal_max],
|
157
164
|
[:order, :scale_random_percent, :boolean]]
|
158
165
|
],
|
@@ -169,12 +176,12 @@ module IB
|
|
169
176
|
[:order, :clearing_account, :string],
|
170
177
|
[:order, :clearing_intent, :string],
|
171
178
|
[:order, :not_held, :boolean],
|
172
|
-
[:
|
179
|
+
[:underlying_present, :boolean],
|
173
180
|
|
174
|
-
[proc { | | filled?(@data[:
|
175
|
-
[:
|
176
|
-
[:
|
177
|
-
[:
|
181
|
+
[proc { | | filled?(@data[:underlying_present]) },
|
182
|
+
[:underlying, :con_id, :int],
|
183
|
+
[:underlying, :delta, :decimal],
|
184
|
+
[:underlying, :price, :decimal]
|
178
185
|
],
|
179
186
|
|
180
187
|
[:order, :algo_strategy, :string],
|
@@ -32,7 +32,7 @@ module IB
|
|
32
32
|
# order to be held. For example, when TWS is trying to locate shares for
|
33
33
|
# a short sell, the value used to indicate this is 'locate'.
|
34
34
|
OrderStatus = def_message [3, 6],
|
35
|
-
[:order_state, :
|
35
|
+
[:order_state, :local_id, :int],
|
36
36
|
[:order_state, :status, :string],
|
37
37
|
[:order_state, :filled, :int],
|
38
38
|
[:order_state, :remaining, :int],
|
@@ -49,10 +49,12 @@ module IB
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Accessors to make OpenOrder and OrderStatus messages API-compatible
|
52
|
-
def
|
53
|
-
order_state.
|
52
|
+
def local_id
|
53
|
+
order_state.local_id
|
54
54
|
end
|
55
55
|
|
56
|
+
alias order_id local_id
|
57
|
+
|
56
58
|
def status
|
57
59
|
order_state.status
|
58
60
|
end
|
@@ -24,17 +24,21 @@ module IB
|
|
24
24
|
|
25
25
|
@results = Array.new(@data[:count]) do |_|
|
26
26
|
{:rank => socket.read_int,
|
27
|
-
:contract =>
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
:contract =>
|
28
|
+
Contract.build(
|
29
|
+
:con_id => socket.read_int,
|
30
|
+
:symbol => socket.read_str,
|
31
|
+
:sec_type => socket.read_str,
|
32
|
+
:expiry => socket.read_str,
|
33
|
+
:strike => socket.read_decimal,
|
34
|
+
:right => socket.read_str,
|
35
|
+
:exchange => socket.read_str,
|
36
|
+
:currency => socket.read_str,
|
37
|
+
:local_symbol => socket.read_str,
|
38
|
+
:contract_detail =>
|
39
|
+
IB::ContractDetail.new(
|
40
|
+
:market_name => socket.read_str,
|
41
|
+
:trading_class => socket.read_str)),
|
38
42
|
:distance => socket.read_str,
|
39
43
|
:benchmark => socket.read_str,
|
40
44
|
:projection => socket.read_str,
|
@@ -26,11 +26,6 @@ module IB
|
|
26
26
|
|
27
27
|
AccountUpdateTime = def_message 8, [:time_stamp, :string]
|
28
28
|
|
29
|
-
# This message is always sent by TWS automatically at connect.
|
30
|
-
# The IB::Connection class subscribes to it automatically and stores
|
31
|
-
# the order id in its @next_order_id attribute.
|
32
|
-
NextValidID = NextValidId = def_message(9, [:order_id, :int])
|
33
|
-
|
34
29
|
NewsBulletins =
|
35
30
|
def_message 14, [:request_id, :int], # unique incrementing bulletin ID.
|
36
31
|
[:type, :int], # Type of bulletin. Valid values include:
|
@@ -87,6 +82,7 @@ module IB
|
|
87
82
|
require 'ib-ruby/messages/incoming/execution_data'
|
88
83
|
require 'ib-ruby/messages/incoming/historical_data'
|
89
84
|
require 'ib-ruby/messages/incoming/market_depths'
|
85
|
+
require 'ib-ruby/messages/incoming/next_valid_id'
|
90
86
|
require 'ib-ruby/messages/incoming/open_order'
|
91
87
|
require 'ib-ruby/messages/incoming/order_status'
|
92
88
|
require 'ib-ruby/messages/incoming/portfolio_value'
|
@@ -36,7 +36,8 @@ module IB
|
|
36
36
|
def encode server
|
37
37
|
[self.class.message_id,
|
38
38
|
self.class.version,
|
39
|
-
@data[:id] || @data[:ticker_id] || @data[:request_id]
|
39
|
+
@data[:id] || @data[:ticker_id] || @data[:request_id] ||
|
40
|
+
@data[:local_id] || @data[:order_id] || [],
|
40
41
|
self.class.data_map.map do |(field, default_method, args)|
|
41
42
|
case
|
42
43
|
when default_method.nil?
|
@@ -45,7 +45,7 @@ module IB
|
|
45
45
|
CancelCalculateImpliedVolatility = CancelImpliedVolatility = def_message(56)
|
46
46
|
CancelCalculateOptionPrice = CancelOptionPrice = def_message(57)
|
47
47
|
|
48
|
-
## Data format is: @data ={ :id =>
|
48
|
+
## Data format is: @data ={ :id => local_id of order to cancel }
|
49
49
|
CancelOrder = def_message 4
|
50
50
|
|
51
51
|
## These messages contain just one or two extra fields:
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'ib-ruby/models/contract'
|
2
|
+
|
3
|
+
module IB
|
4
|
+
module Models
|
5
|
+
|
6
|
+
# "BAG" is not really a contract, but a combination (combo) of securities.
|
7
|
+
# AKA basket or bag of securities. Individual securities in combo are represented
|
8
|
+
# by ComboLeg objects.
|
9
|
+
class Bag < Contract
|
10
|
+
# General Notes:
|
11
|
+
# 1. :exchange for the leg definition must match that of the combination order.
|
12
|
+
# The exception is for a STK legs, which must specify the SMART exchange.
|
13
|
+
# 2. :symbol => "USD" For combo Contract, this is an arbitrary value (like "USD")
|
14
|
+
|
15
|
+
validates_format_of :sec_type, :with => /^bag$/, :message => "should be a bag"
|
16
|
+
validates_format_of :right, :with => /^none$/, :message => "should be none"
|
17
|
+
validates_format_of :expiry, :with => /^$/, :message => "should be blank"
|
18
|
+
validate :legs_cannot_be_empty
|
19
|
+
|
20
|
+
def legs_cannot_be_empty
|
21
|
+
errors.add(:legs, "legs cannot be empty") if legs.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_attributes
|
25
|
+
{:legs => Array.new,
|
26
|
+
:sec_type => :bag}.merge super
|
27
|
+
end
|
28
|
+
|
29
|
+
def description
|
30
|
+
self[:description] || to_human
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_human
|
34
|
+
"<Bag: #{[symbol, exchange, currency].join(' ')} legs: #{legs_description} >"
|
35
|
+
end
|
36
|
+
|
37
|
+
### Leg-related methods
|
38
|
+
|
39
|
+
# TODO: Rewrite with legs and legs_description being strictly in sync...
|
40
|
+
# TODO: Find a way to serialize legs without references...
|
41
|
+
# IB-equivalent leg description.
|
42
|
+
def legs_description
|
43
|
+
self[:legs_description] || legs.map { |leg| "#{leg.con_id}|#{leg.weight}" }.join(',')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if two Contracts have same legs (maybe in different order)
|
47
|
+
def same_legs? other
|
48
|
+
legs == other.legs ||
|
49
|
+
legs_description.split(',').sort == other.legs_description.split(',').sort
|
50
|
+
end
|
51
|
+
|
52
|
+
# Contract comparison
|
53
|
+
def == other
|
54
|
+
super && same_legs?(other)
|
55
|
+
end
|
56
|
+
|
57
|
+
end # class Bag
|
58
|
+
end # Models
|
59
|
+
end # IB
|
@@ -7,6 +7,8 @@ module IB
|
|
7
7
|
class ComboLeg < Model.for(:combo_leg)
|
8
8
|
include ModelProperties
|
9
9
|
|
10
|
+
belongs_to :contract
|
11
|
+
|
10
12
|
# General Notes:
|
11
13
|
# 1. The exchange for the leg definition must match that of the combination order.
|
12
14
|
# The exception is for a STK leg definition, which must specify the SMART exchange.
|
@@ -36,12 +38,14 @@ module IB
|
|
36
38
|
validates_format_of :designated_location, :with => /^$/,
|
37
39
|
:message => "should be blank or orders will be rejected"
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
def default_attributes
|
42
|
+
{:con_id => 0,
|
43
|
+
:open_close => :same, # The only option for retail customers.
|
44
|
+
:short_sale_slot => :default,
|
45
|
+
:designated_location => '',
|
46
|
+
:exchange => 'SMART', # Unless SMART, Order modification fails
|
47
|
+
:exempt_code => -1, }.merge super
|
48
|
+
end
|
45
49
|
|
46
50
|
# Leg's weight is a combination of action and ratio
|
47
51
|
def weight
|