ib-ruby 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|