ib-ruby 0.7.6 → 0.7.8
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/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
|