ib-ruby 0.6.1 → 0.7.0
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 +4 -0
- data/README.md +25 -17
- data/VERSION +1 -1
- data/bin/account_info +1 -1
- data/bin/cancel_orders +2 -1
- data/bin/contract_details +3 -2
- data/bin/depth_of_market +1 -1
- data/bin/historic_data +1 -1
- data/bin/historic_data_cli +57 -104
- data/bin/list_orders +4 -3
- data/bin/market_data +1 -1
- data/bin/option_data +1 -1
- data/bin/place_combo_order +63 -0
- data/bin/place_order +2 -4
- data/bin/template +1 -1
- data/bin/{generic_data.rb → tick_data} +3 -1
- data/bin/time_and_sales +1 -1
- data/lib/ib-ruby.rb +1 -0
- data/lib/ib-ruby/connection.rb +68 -68
- data/lib/ib-ruby/errors.rb +28 -0
- data/lib/ib-ruby/extensions.rb +7 -0
- data/lib/ib-ruby/messages.rb +1 -0
- data/lib/ib-ruby/messages/abstract_message.rb +16 -11
- data/lib/ib-ruby/messages/incoming.rb +125 -329
- data/lib/ib-ruby/messages/incoming/open_order.rb +193 -0
- data/lib/ib-ruby/messages/incoming/ticks.rb +131 -0
- data/lib/ib-ruby/messages/outgoing.rb +44 -45
- data/lib/ib-ruby/models/combo_leg.rb +16 -1
- data/lib/ib-ruby/models/contract.rb +18 -10
- data/lib/ib-ruby/models/contract/bag.rb +1 -7
- data/lib/ib-ruby/models/execution.rb +2 -1
- data/lib/ib-ruby/models/model.rb +1 -1
- data/lib/ib-ruby/models/order.rb +116 -56
- data/lib/ib-ruby/socket.rb +24 -3
- data/spec/account_helper.rb +2 -1
- data/spec/ib-ruby/messages/outgoing_spec.rb +1 -1
- data/spec/ib-ruby/models/combo_leg_spec.rb +0 -1
- data/spec/integration/account_info_spec.rb +2 -2
- data/spec/integration/contract_info_spec.rb +4 -4
- data/spec/integration/depth_data_spec.rb +3 -3
- data/spec/integration/historic_data_spec.rb +1 -1
- data/spec/integration/market_data_spec.rb +4 -4
- data/spec/integration/option_data_spec.rb +1 -1
- data/spec/integration/orders/combo_spec.rb +51 -0
- data/spec/integration/orders/execution_spec.rb +15 -8
- data/spec/integration/orders/placement_spec.rb +46 -72
- data/spec/integration/orders/valid_ids_spec.rb +6 -6
- data/spec/integration_helper.rb +0 -79
- data/spec/order_helper.rb +153 -0
- metadata +13 -4
@@ -0,0 +1,193 @@
|
|
1
|
+
# OpenOrder is the longest message with complex processing logics, it is isolated here
|
2
|
+
module IB
|
3
|
+
module Messages
|
4
|
+
module Incoming
|
5
|
+
|
6
|
+
OpenOrder =
|
7
|
+
def_message [5, [23, 28]],
|
8
|
+
[:order, :order_id, :int],
|
9
|
+
|
10
|
+
[:contract, :con_id, :int],
|
11
|
+
[:contract, :symbol, :string],
|
12
|
+
[:contract, :sec_type, :string],
|
13
|
+
[:contract, :expiry, :string],
|
14
|
+
[:contract, :strike, :decimal],
|
15
|
+
[:contract, :right, :string],
|
16
|
+
[:contract, :exchange, :string],
|
17
|
+
[:contract, :currency, :string],
|
18
|
+
[:contract, :local_symbol, :string],
|
19
|
+
|
20
|
+
[:order, :action, :string],
|
21
|
+
[:order, :total_quantity, :int],
|
22
|
+
[:order, :order_type, :string],
|
23
|
+
[:order, :limit_price, :decimal_max],
|
24
|
+
[:order, :aux_price, :decimal_max],
|
25
|
+
[:order, :tif, :string],
|
26
|
+
[:order, :oca_group, :string],
|
27
|
+
[:order, :account, :string],
|
28
|
+
[:order, :open_close, :string],
|
29
|
+
[:order, :origin, :int],
|
30
|
+
[:order, :order_ref, :string],
|
31
|
+
[:order, :client_id, :int],
|
32
|
+
[:order, :perm_id, :int],
|
33
|
+
[:order, :outside_rth, :boolean], # (@socket.read_int == 1)
|
34
|
+
[:order, :hidden, :boolean], # (@socket.read_int == 1)
|
35
|
+
[:order, :discretionary_amount, :decimal],
|
36
|
+
[:order, :good_after_time, :string],
|
37
|
+
[:shares_allocation, :string], # deprecated! field
|
38
|
+
|
39
|
+
[:order, :fa_group, :string],
|
40
|
+
[:order, :fa_method, :string],
|
41
|
+
[:order, :fa_percentage, :string],
|
42
|
+
[:order, :fa_profile, :string],
|
43
|
+
[:order, :good_till_date, :string],
|
44
|
+
[:order, :rule_80a, :string],
|
45
|
+
[:order, :percent_offset, :decimal_max],
|
46
|
+
[:order, :settling_firm, :string],
|
47
|
+
[:order, :short_sale_slot, :int],
|
48
|
+
[:order, :designated_location, :string],
|
49
|
+
[:order, :exempt_code, :int], # skipped in ver 51?
|
50
|
+
[:order, :auction_strategy, :int],
|
51
|
+
[:order, :starting_price, :decimal_max],
|
52
|
+
[:order, :stock_ref_price, :decimal_max],
|
53
|
+
[:order, :delta, :decimal_max],
|
54
|
+
[:order, :stock_range_lower, :decimal_max],
|
55
|
+
[:order, :stock_range_upper, :decimal_max],
|
56
|
+
[:order, :display_size, :int],
|
57
|
+
#@order.rth_only = @socket.read_boolean
|
58
|
+
[:order, :block_order, :boolean],
|
59
|
+
[:order, :sweep_to_fill, :boolean],
|
60
|
+
[:order, :all_or_none, :boolean],
|
61
|
+
[:order, :min_quantity, :int_max],
|
62
|
+
[:order, :oca_type, :int],
|
63
|
+
[:order, :etrade_only, :boolean],
|
64
|
+
[:order, :firm_quote_only, :boolean],
|
65
|
+
[:order, :nbbo_price_cap, :decimal_max],
|
66
|
+
[:order, :parent_id, :int],
|
67
|
+
[:order, :trigger_method, :int],
|
68
|
+
[:order, :volatility, :decimal_max],
|
69
|
+
[:order, :volatility_type, :int],
|
70
|
+
[:order, :delta_neutral_order_type, :string],
|
71
|
+
[:order, :delta_neutral_aux_price, :decimal_max]
|
72
|
+
|
73
|
+
|
74
|
+
class OpenOrder
|
75
|
+
|
76
|
+
def load
|
77
|
+
super
|
78
|
+
|
79
|
+
load_map [27, [proc { | | filled?(@data[:order][:delta_neutral_order_type]) },
|
80
|
+
# As of client v.52, we receive delta... params in openOrder
|
81
|
+
[:order, :delta_neutral_con_id, :int],
|
82
|
+
[:order, :delta_neutral_settling_firm, :string],
|
83
|
+
[:order, :delta_neutral_clearing_account, :string],
|
84
|
+
[:order, :delta_neutral_clearing_intent, :string]]
|
85
|
+
],
|
86
|
+
[:order, :continuous_update, :int],
|
87
|
+
[:order, :reference_price_type, :int],
|
88
|
+
[:order, :trail_stop_price, :decimal_max],
|
89
|
+
|
90
|
+
# As of client v.56, we receive trailing_percent in openOrder
|
91
|
+
[30, [:order, :trailing_percent, :decimal_max]], # Never! 28 currently
|
92
|
+
|
93
|
+
[:order, :basis_points, :decimal_max],
|
94
|
+
[:order, :basis_points_type, :int_max],
|
95
|
+
[:contract, :legs_description, :string],
|
96
|
+
|
97
|
+
# Never happens! 28 is the max supported version currently
|
98
|
+
# As of client v.55, we receive orderComboLegs (price) in openOrder
|
99
|
+
[29, [:contract, :legs, :array, proc do |_|
|
100
|
+
Models::ComboLeg.new :con_id => socket.read_int,
|
101
|
+
:ratio => socket.read_int,
|
102
|
+
:action => socket.read_string,
|
103
|
+
:exchange => socket.read_string,
|
104
|
+
:open_close => socket.read_int,
|
105
|
+
:short_sale_slot => socket.read_int,
|
106
|
+
:designated_location => socket.read_string,
|
107
|
+
:exempt_code => socket.read_int
|
108
|
+
end],
|
109
|
+
|
110
|
+
# Order keeps received leg prices in a separate Array for some reason ?!
|
111
|
+
[:order, :leg_prices, :array, proc { |_| socket.read_decimal_max }],
|
112
|
+
],
|
113
|
+
# As of client v.51, we can receive smartComboRoutingParams in openOrder
|
114
|
+
[26, [:smart_combo_routing_params, :hash]],
|
115
|
+
|
116
|
+
[:order, :scale_init_level_size, :int_max],
|
117
|
+
[:order, :scale_subs_level_size, :int_max],
|
118
|
+
[:order, :scale_price_increment, :decimal_max],
|
119
|
+
|
120
|
+
# As of client v.54, we can receive scale order fields
|
121
|
+
[28, [proc { | | filled?(@data[:order][:scale_price_increment]) },
|
122
|
+
[:order, :scale_price_adjust_value, :decimal_max],
|
123
|
+
[:order, :scale_price_adjust_interval, :int_max],
|
124
|
+
[:order, :scale_profit_offset, :decimal_max],
|
125
|
+
[:order, :scale_auto_reset, :boolean],
|
126
|
+
[:order, :scale_init_position, :int_max],
|
127
|
+
[:order, :scale_init_position, :int_max],
|
128
|
+
[:order, :scale_init_fill_qty, :decimal_max],
|
129
|
+
[:order, :scale_random_percent, :boolean]]
|
130
|
+
],
|
131
|
+
|
132
|
+
# As of client v.49/50, we can receive hedgeType, hedgeParam, optOutSmartRouting
|
133
|
+
[25,
|
134
|
+
[:order, :hedge_type, :string],
|
135
|
+
[proc { | | filled?(@data[:order][:hedge_type]) },
|
136
|
+
[:order, :hedge_param, :string],
|
137
|
+
],
|
138
|
+
[:order, :opt_out_smart_routing, :boolean]
|
139
|
+
],
|
140
|
+
|
141
|
+
[:order, :clearing_account, :string],
|
142
|
+
[:order, :clearing_intent, :string],
|
143
|
+
[:order, :not_held, :boolean],
|
144
|
+
[:contract, :under_comp, :boolean],
|
145
|
+
|
146
|
+
[proc { | | filled?(@data[:contract][:under_comp]) },
|
147
|
+
[:contract, :under_con_id, :int],
|
148
|
+
[:contract, :under_delta, :decimal],
|
149
|
+
[:contract, :under_price, :decimal]
|
150
|
+
],
|
151
|
+
|
152
|
+
[:order, :algo_strategy, :string],
|
153
|
+
|
154
|
+
# TODO: Test Order with algo_params, scale and legs!
|
155
|
+
[proc { | | filled?(@data[:order][:algo_strategy]) },
|
156
|
+
[:order, :algo_params, :hash]
|
157
|
+
],
|
158
|
+
|
159
|
+
[:order, :what_if, :boolean],
|
160
|
+
[:order, :status, :string],
|
161
|
+
[:order, :init_margin, :string],
|
162
|
+
[:order, :maint_margin, :string],
|
163
|
+
[:order, :equity_with_loan, :string],
|
164
|
+
[:order, :commission, :decimal_max], # May be nil!
|
165
|
+
[:order, :min_commission, :decimal_max], # May be nil!
|
166
|
+
[:order, :max_commission, :decimal_max], # May be nil!
|
167
|
+
[:order, :commission_currency, :string],
|
168
|
+
[:order, :warning_text, :string]
|
169
|
+
|
170
|
+
@order = Models::Order.new @data[:order]
|
171
|
+
@contract = Models::Contract.build @data[:contract]
|
172
|
+
end
|
173
|
+
|
174
|
+
# Check if given value was set by TWS to something vaguely "positive"
|
175
|
+
def filled? value
|
176
|
+
case value
|
177
|
+
when String
|
178
|
+
!value.empty?
|
179
|
+
when Float, Integer
|
180
|
+
value > 0
|
181
|
+
else
|
182
|
+
!!value # to_bool
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_human
|
187
|
+
"<OpenOrder: #{@contract.to_human} #{@order.to_human}>"
|
188
|
+
end
|
189
|
+
|
190
|
+
end # class OpenOrder
|
191
|
+
end # module Incoming
|
192
|
+
end # module Messages
|
193
|
+
end # module IB
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# All message classes related to ticks located here
|
2
|
+
module IB
|
3
|
+
module Messages
|
4
|
+
module Incoming
|
5
|
+
|
6
|
+
class AbstractTick < AbstractMessage
|
7
|
+
# Returns Symbol with a meaningful name for received tick type
|
8
|
+
def type
|
9
|
+
TICK_TYPES[@data[:tick_type]]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_human
|
13
|
+
"<#{self.message_type} #{type}:" +
|
14
|
+
@data.map do |key, value|
|
15
|
+
" #{key} #{value}" unless [:version, :ticker_id, :tick_type].include?(key)
|
16
|
+
end.compact.join(',') + " >"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# The IB code seems to dispatch up to two wrapped objects for this message, a tickPrice
|
21
|
+
# and sometimes a tickSize, which seems to be identical to the TICK_SIZE object.
|
22
|
+
#
|
23
|
+
# Important note from
|
24
|
+
# http://chuckcaplan.com/twsapi/index.php/void%20tickPrice%28%29 :
|
25
|
+
#
|
26
|
+
# "The low you get is NOT the low for the day as you'd expect it
|
27
|
+
# to be. It appears IB calculates the low based on all
|
28
|
+
# transactions after 4pm the previous day. The most inaccurate
|
29
|
+
# results occur when the stock moves up in the 4-6pm aftermarket
|
30
|
+
# on the previous day and then gaps open upward in the
|
31
|
+
# morning. The low you receive from TWS can be easily be several
|
32
|
+
# points different from the actual 9:30am-4pm low for the day in
|
33
|
+
# cases like this. If you require a correct traded low for the
|
34
|
+
# day, you can't get it from the TWS API. One possible source to
|
35
|
+
# help build the right data would be to compare against what Yahoo
|
36
|
+
# lists on finance.yahoo.com/q?s=ticker under the "Day's Range"
|
37
|
+
# statistics (be careful here, because Yahoo will use anti-Denial
|
38
|
+
# of Service techniques to hang your connection if you try to
|
39
|
+
# request too many bytes in a short period of time from them). For
|
40
|
+
# most purposes, a good enough approach would start by replacing
|
41
|
+
# the TWS low for the day with Yahoo's day low when you first
|
42
|
+
# start watching a stock ticker; let's call this time T. Then,
|
43
|
+
# update your internal low if the bid or ask tick you receive is
|
44
|
+
# lower than that for the remainder of the day. You should check
|
45
|
+
# against Yahoo again at time T+20min to handle the occasional
|
46
|
+
# case where the stock set a new low for the day in between
|
47
|
+
# T-20min (the real time your original quote was from, taking into
|
48
|
+
# account the delay) and time T. After that you should have a
|
49
|
+
# correct enough low for the rest of the day as long as you keep
|
50
|
+
# updating based on the bid/ask. It could still get slightly off
|
51
|
+
# in a case where a short transaction setting a new low appears in
|
52
|
+
# between ticks of data that TWS sends you. The high is probably
|
53
|
+
# distorted in the same way the low is, which would throw your
|
54
|
+
# results off if the stock traded after-hours and gapped down. It
|
55
|
+
# should be corrected in a similar way as described above if this
|
56
|
+
# is important to you."
|
57
|
+
#
|
58
|
+
# IB then emits at most 2 events on eWrapper:
|
59
|
+
# tickPrice( tickerId, tickType, price, canAutoExecute)
|
60
|
+
# tickSize( tickerId, sizeTickType, size)
|
61
|
+
TickPrice = def_message [1, 6], AbstractTick,
|
62
|
+
[:ticker_id, :int],
|
63
|
+
[:tick_type, :int],
|
64
|
+
[:price, :decimal],
|
65
|
+
[:size, :int],
|
66
|
+
[:can_auto_execute, :int]
|
67
|
+
|
68
|
+
TickSize = def_message [2, 6], AbstractTick,
|
69
|
+
[:ticker_id, :int],
|
70
|
+
[:tick_type, :int],
|
71
|
+
[:size, :int]
|
72
|
+
|
73
|
+
TickGeneric = def_message [45, 6], AbstractTick,
|
74
|
+
[:ticker_id, :int],
|
75
|
+
[:tick_type, :int],
|
76
|
+
[:value, :decimal]
|
77
|
+
|
78
|
+
TickString = def_message [46, 6], AbstractTick,
|
79
|
+
[:ticker_id, :int],
|
80
|
+
[:tick_type, :int],
|
81
|
+
[:value, :string]
|
82
|
+
|
83
|
+
TickEFP = def_message [47, 6], AbstractTick,
|
84
|
+
[:ticker_id, :int],
|
85
|
+
[:tick_type, :int],
|
86
|
+
[:basis_points, :decimal],
|
87
|
+
[:formatted_basis_points, :string],
|
88
|
+
[:implied_futures_price, :decimal],
|
89
|
+
[:hold_days, :int],
|
90
|
+
[:dividend_impact, :decimal],
|
91
|
+
[:dividends_to_expiry, :decimal]
|
92
|
+
|
93
|
+
# This message is received when the market in an option or its underlier moves.
|
94
|
+
# TWS�s option model volatilities, prices, and deltas, along with the present
|
95
|
+
# value of dividends expected on that options underlier are received.
|
96
|
+
# TickOption message contains following @data:
|
97
|
+
# :ticker_id - Id that was specified previously in the call to reqMktData()
|
98
|
+
# :tick_type - Specifies the type of option computation (see TICK_TYPES).
|
99
|
+
# :implied_volatility - The implied volatility calculated by the TWS option
|
100
|
+
# modeler, using the specified :tick_type value.
|
101
|
+
# :delta - The option delta value.
|
102
|
+
# :option_price - The option price.
|
103
|
+
# :pv_dividend - The present value of dividends expected on the options underlier
|
104
|
+
# :gamma - The option gamma value.
|
105
|
+
# :vega - The option vega value.
|
106
|
+
# :theta - The option theta value.
|
107
|
+
# :under_price - The price of the underlying.
|
108
|
+
TickOptionComputation = TickOption =
|
109
|
+
def_message([21, 6], AbstractTick,
|
110
|
+
[:ticker_id, :int],
|
111
|
+
[:tick_type, :int],
|
112
|
+
# What is the "not yet computed" indicator:
|
113
|
+
[:implied_volatility, :decimal_limit_1], # -1 and below
|
114
|
+
[:delta, :decimal_limit_2], # -2 and below
|
115
|
+
[:option_price, :decimal_limit_1], # -1 -"-
|
116
|
+
[:pv_dividend, :decimal_limit_1], # -1 -"-
|
117
|
+
[:gamma, :decimal_limit_2], # -2 -"-
|
118
|
+
[:vega, :decimal_limit_2], # -2 -"-
|
119
|
+
[:theta, :decimal_limit_2], # -2 -"-
|
120
|
+
[:under_price, :decimal_limit_1]) do
|
121
|
+
|
122
|
+
"<TickOption #{type} for #{:ticker_id}: underlying @ #{under_price}, "+
|
123
|
+
"option @ #{option_price}, IV #{implied_volatility}%, delta #{delta}, " +
|
124
|
+
"gamma #{gamma}, vega #{vega}, theta #{theta}, pv_dividend #{pv_dividend}>"
|
125
|
+
end
|
126
|
+
|
127
|
+
TickSnapshotEnd = def_message 57, [:ticker_id, :int]
|
128
|
+
|
129
|
+
end # module Incoming
|
130
|
+
end # module Messages
|
131
|
+
end # module IB
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'ib-ruby/messages/abstract_message'
|
2
2
|
|
3
|
-
# EClientSocket.java uses sendMax() rather than send() for a number of these.
|
4
|
-
# It sends an EOL rather than a number if the value == Integer.MAX_VALUE (or Double.MAX_VALUE).
|
5
|
-
# These fields are initialized to this MAX_VALUE.
|
6
|
-
# This has been implemented with nils in Ruby to represent the case where an EOL should be sent.
|
7
|
-
|
8
3
|
# TODO: Don't instantiate messages, use their classes as just namespace for .encode/decode
|
9
4
|
|
10
5
|
module IB
|
11
6
|
module Messages
|
7
|
+
|
8
|
+
# Outgoing IB messages (sent to TWS/Gateway)
|
12
9
|
module Outgoing
|
13
10
|
extend Messages # def_message macros
|
14
11
|
|
12
|
+
# Container for specific message classes, keyed by their message_ids
|
13
|
+
Classes = {}
|
14
|
+
|
15
15
|
class AbstractMessage < IB::Messages::AbstractMessage
|
16
16
|
|
17
17
|
def initialize data={}
|
@@ -28,21 +28,16 @@ module IB
|
|
28
28
|
# an Array of elements that ought to be sent to the server by calling to_s on
|
29
29
|
# each one and postpending a '\0'.
|
30
30
|
#
|
31
|
-
def send_to
|
32
|
-
self.encode.flatten.each do |datum|
|
33
|
-
|
34
|
-
datum = "1" if datum == true
|
35
|
-
datum = "0" if datum == false
|
36
|
-
|
37
|
-
#p datum.to_s + EOL
|
38
|
-
server[:socket].syswrite(datum.to_s + EOL)
|
31
|
+
def send_to server
|
32
|
+
self.encode(server).flatten.each do |datum|
|
33
|
+
server[:socket].write_data datum
|
39
34
|
end
|
40
35
|
end
|
41
36
|
|
42
37
|
# At minimum, Outgoing message contains message_id and version.
|
43
38
|
# Most messages also contain (ticker, request or order) :id.
|
44
39
|
# Then, content of @data Hash is encoded per instructions in data_map.
|
45
|
-
def encode
|
40
|
+
def encode server
|
46
41
|
[self.class.message_id,
|
47
42
|
self.class.version,
|
48
43
|
@data[:id] || @data[:ticker_id] || @data[:request_id]|| @data[:order_id] || [],
|
@@ -122,6 +117,11 @@ module IB
|
|
122
117
|
RequestFA = def_message 18, :fa_data_type
|
123
118
|
# data = { :fa_data_type => int, :xml => String }
|
124
119
|
ReplaceFA = def_message 19, :fa_data_type, :xml
|
120
|
+
# data = { :market_data_type => int }
|
121
|
+
# The API can now receive frozen market data from Trader Workstation. Frozen
|
122
|
+
# market data is the last data recorded in our system. Use this method with
|
123
|
+
# :market_data_type = 1 for real-time streaming, 2 for frozen market data
|
124
|
+
RequestMarketDataType = def_message 59, :market_data_type
|
125
125
|
|
126
126
|
# @data = { :subscribe => boolean,
|
127
127
|
# :account_code => Advisor accounts only. Empty ('') for a standard account. }
|
@@ -292,50 +292,55 @@ module IB
|
|
292
292
|
:instrument,
|
293
293
|
:location_code,
|
294
294
|
:scan_code,
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
295
|
+
:above_price,
|
296
|
+
:below_price,
|
297
|
+
:above_volume,
|
298
|
+
:market_cap_above,
|
299
|
+
:market_cap_below,
|
300
300
|
:moody_rating_above,
|
301
301
|
:moody_rating_below,
|
302
302
|
:sp_rating_above,
|
303
303
|
:sp_rating_below,
|
304
304
|
:maturity_date_above,
|
305
305
|
:maturity_date_below,
|
306
|
-
|
307
|
-
|
306
|
+
:coupon_rate_above,
|
307
|
+
:coupon_rate_below,
|
308
308
|
:exclude_convertible,
|
309
|
-
|
309
|
+
:average_option_volume_above, # ?
|
310
310
|
:scanner_setting_pairs,
|
311
311
|
:stock_type_filter)
|
312
312
|
|
313
313
|
### Even more complex Outgoing Message classes, overriding #encode method:
|
314
314
|
|
315
|
-
|
315
|
+
|
316
|
+
# Data format is { :id => int: order_id,
|
316
317
|
# :contract => Contract,
|
317
318
|
# :order => Order }
|
318
|
-
|
319
|
-
|
320
|
-
|
319
|
+
PlaceOrder = def_message [3, 31] # 38 Need to set up Classes Hash properly
|
320
|
+
|
321
|
+
class PlaceOrder
|
322
|
+
def encode server
|
323
|
+
|
324
|
+
# Old server version supports no enhancements
|
325
|
+
@version = 31 if server[:server_version] <= 60
|
321
326
|
|
322
|
-
def encode
|
323
327
|
[super,
|
324
|
-
@data[:order].serialize_with(@data[:contract])].flatten
|
328
|
+
@data[:order].serialize_with(server, @data[:contract])].flatten
|
325
329
|
end
|
326
330
|
end # PlaceOrder
|
327
331
|
|
328
|
-
|
332
|
+
# Messages that request bar data have special processing of @data
|
333
|
+
class BarRequestMessage < AbstractMessage
|
329
334
|
# Preprocessor for some data fields
|
330
335
|
def parse data
|
331
336
|
data_type = DATA_TYPES[data[:what_to_show]] || data[:what_to_show]
|
332
337
|
unless DATA_TYPES.values.include?(data_type)
|
333
|
-
|
338
|
+
error ":what_to_show must be one of #{DATA_TYPES.inspect}", :args
|
334
339
|
end
|
335
340
|
|
336
341
|
bar_size = BAR_SIZES[data[:bar_size]] || data[:bar_size]
|
337
342
|
unless BAR_SIZES.values.include?(bar_size)
|
338
|
-
|
343
|
+
error ":bar_size must be one of #{BAR_SIZES.inspect}", :args
|
339
344
|
end
|
340
345
|
|
341
346
|
contract = data[:contract].is_a?(Models::Contract) ?
|
@@ -347,7 +352,7 @@ module IB
|
|
347
352
|
|
348
353
|
# data = { :id => ticker_id (int),
|
349
354
|
# :contract => Contract ,
|
350
|
-
# :bar_size => int/Symbol? Currently only 5 second bars
|
355
|
+
# :bar_size => int/Symbol? Currently only 5 second bars are supported,
|
351
356
|
# if any other value is used, an exception will be thrown.,
|
352
357
|
# :what_to_show => Symbol: Determines the nature of data being extracted.
|
353
358
|
# Valid values:
|
@@ -361,13 +366,10 @@ module IB
|
|
361
366
|
# "Regular Trading Hours" of the product in question is returned,
|
362
367
|
# even if the time span requested falls partially or completely
|
363
368
|
# outside of them.
|
364
|
-
|
365
|
-
@message_id = 50
|
366
|
-
@version = 1 # ?
|
367
|
-
|
368
|
-
include DataParser
|
369
|
+
RequestRealTimeBars = def_message 50, BarRequestMessage
|
369
370
|
|
370
|
-
|
371
|
+
class RequestRealTimeBars
|
372
|
+
def encode server
|
371
373
|
data_type, bar_size, contract = parse @data
|
372
374
|
|
373
375
|
[super,
|
@@ -449,13 +451,10 @@ module IB
|
|
449
451
|
# For backfill on futures data, you may need to leave the Primary
|
450
452
|
# Exchange field of the Contract structure blank; see
|
451
453
|
# http://www.interactivebrokers.com/discus/messages/2/28477.html?1114646754
|
452
|
-
|
453
|
-
@message_id = 20
|
454
|
-
@version = 4
|
454
|
+
RequestHistoricalData = def_message [20, 4], BarRequestMessage
|
455
455
|
|
456
|
-
|
457
|
-
|
458
|
-
def encode
|
456
|
+
class RequestHistoricalData
|
457
|
+
def encode server
|
459
458
|
data_type, bar_size, contract = parse @data
|
460
459
|
|
461
460
|
[super,
|
@@ -470,7 +469,6 @@ module IB
|
|
470
469
|
end
|
471
470
|
end # RequestHistoricalData
|
472
471
|
|
473
|
-
|
474
472
|
end # module Outgoing
|
475
473
|
end # module Messages
|
476
474
|
end # module IB
|
@@ -512,3 +510,4 @@ __END__
|
|
512
510
|
private static final int CANCEL_CALC_IMPLIED_VOLAT = 56;
|
513
511
|
private static final int CANCEL_CALC_OPTION_PRICE = 57;
|
514
512
|
private static final int REQ_GLOBAL_CANCEL = 58;
|
513
|
+
private static final int REQ_MARKET_DATA_TYPE = 59;
|