ib-api 972.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.
- checksums.yaml +7 -0
- data/.gitignore +50 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +105 -0
- data/Guardfile +24 -0
- data/LICENSE +674 -0
- data/README.md +65 -0
- data/Rakefile +11 -0
- data/VERSION +1 -0
- data/api.gemspec +43 -0
- data/bin/console +95 -0
- data/bin/console.yml +3 -0
- data/bin/setup +8 -0
- data/changelog.md +7 -0
- data/example/README.md +76 -0
- data/example/account_info +54 -0
- data/example/account_positions +30 -0
- data/example/account_summary +88 -0
- data/example/cancel_orders +74 -0
- data/example/fa_accounts +25 -0
- data/example/fundamental_data +40 -0
- data/example/historic_data_cli +186 -0
- data/example/list_orders +45 -0
- data/example/portfolio_csv +81 -0
- data/example/scanner_data +62 -0
- data/example/template +19 -0
- data/example/tick_data +28 -0
- data/lib/extensions/class-extensions.rb +87 -0
- data/lib/ib-api.rb +7 -0
- data/lib/ib/base.rb +103 -0
- data/lib/ib/base_properties.rb +160 -0
- data/lib/ib/connection.rb +450 -0
- data/lib/ib/constants.rb +393 -0
- data/lib/ib/errors.rb +44 -0
- data/lib/ib/logger.rb +26 -0
- data/lib/ib/messages.rb +99 -0
- data/lib/ib/messages/abstract_message.rb +101 -0
- data/lib/ib/messages/incoming.rb +251 -0
- data/lib/ib/messages/incoming/abstract_message.rb +116 -0
- data/lib/ib/messages/incoming/account_value.rb +78 -0
- data/lib/ib/messages/incoming/alert.rb +34 -0
- data/lib/ib/messages/incoming/contract_data.rb +102 -0
- data/lib/ib/messages/incoming/delta_neutral_validation.rb +23 -0
- data/lib/ib/messages/incoming/execution_data.rb +50 -0
- data/lib/ib/messages/incoming/historical_data.rb +84 -0
- data/lib/ib/messages/incoming/market_depths.rb +44 -0
- data/lib/ib/messages/incoming/next_valid_id.rb +18 -0
- data/lib/ib/messages/incoming/open_order.rb +277 -0
- data/lib/ib/messages/incoming/order_status.rb +85 -0
- data/lib/ib/messages/incoming/portfolio_value.rb +78 -0
- data/lib/ib/messages/incoming/real_time_bar.rb +32 -0
- data/lib/ib/messages/incoming/scanner_data.rb +54 -0
- data/lib/ib/messages/incoming/ticks.rb +268 -0
- data/lib/ib/messages/outgoing.rb +437 -0
- data/lib/ib/messages/outgoing/abstract_message.rb +88 -0
- data/lib/ib/messages/outgoing/account_requests.rb +112 -0
- data/lib/ib/messages/outgoing/bar_requests.rb +250 -0
- data/lib/ib/messages/outgoing/place_order.rb +209 -0
- data/lib/ib/messages/outgoing/request_marketdata.rb +99 -0
- data/lib/ib/messages/outgoing/request_tick_data.rb +21 -0
- data/lib/ib/model.rb +4 -0
- data/lib/ib/models.rb +14 -0
- data/lib/ib/server_versions.rb +114 -0
- data/lib/ib/socket.rb +185 -0
- data/lib/ib/support.rb +160 -0
- data/lib/ib/version.rb +6 -0
- data/lib/models/ib/account.rb +85 -0
- data/lib/models/ib/account_value.rb +33 -0
- data/lib/models/ib/bag.rb +55 -0
- data/lib/models/ib/bar.rb +31 -0
- data/lib/models/ib/combo_leg.rb +105 -0
- data/lib/models/ib/condition.rb +245 -0
- data/lib/models/ib/contract.rb +415 -0
- data/lib/models/ib/contract_detail.rb +108 -0
- data/lib/models/ib/execution.rb +67 -0
- data/lib/models/ib/forex.rb +13 -0
- data/lib/models/ib/future.rb +15 -0
- data/lib/models/ib/index.rb +15 -0
- data/lib/models/ib/option.rb +78 -0
- data/lib/models/ib/option_detail.rb +55 -0
- data/lib/models/ib/order.rb +519 -0
- data/lib/models/ib/order_state.rb +152 -0
- data/lib/models/ib/portfolio_value.rb +64 -0
- data/lib/models/ib/stock.rb +16 -0
- data/lib/models/ib/underlying.rb +34 -0
- data/lib/models/ib/vertical.rb +96 -0
- data/lib/requires.rb +12 -0
- metadata +203 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module IB
|
2
|
+
module Messages
|
3
|
+
module Incoming
|
4
|
+
|
5
|
+
# RealTimeBar contains following @data:
|
6
|
+
# :request_id - The ID of the *request* to which this is responding
|
7
|
+
# :time - The date-time stamp of the start of the bar. The format is offset in
|
8
|
+
# seconds from the beginning of 1970, same format as the UNIX epoch time
|
9
|
+
# :bar - received RT Bar
|
10
|
+
RealTimeBar = def_message [50, 3],
|
11
|
+
[:request_id, :int],
|
12
|
+
[:bar, :time, :int_date],
|
13
|
+
[:bar, :open, :decimal],
|
14
|
+
[:bar, :high, :decimal],
|
15
|
+
[:bar, :low, :decimal],
|
16
|
+
[:bar, :close, :decimal],
|
17
|
+
[:bar, :volume, :int],
|
18
|
+
[:bar, :wap, :decimal],
|
19
|
+
[:bar, :trades, :int]
|
20
|
+
class RealTimeBar
|
21
|
+
def bar
|
22
|
+
@bar = IB::Bar.new @data[:bar]
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_human
|
26
|
+
"<RealTimeBar: #{request_id} #{bar}>"
|
27
|
+
end
|
28
|
+
end # RealTimeBar
|
29
|
+
|
30
|
+
end # module Incoming
|
31
|
+
end # module Messages
|
32
|
+
end # module IB
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module IB
|
2
|
+
module Messages
|
3
|
+
module Incoming
|
4
|
+
|
5
|
+
# This method receives the requested market scanner data results.
|
6
|
+
# ScannerData contains following @data:
|
7
|
+
# :request_id - The ID of the request to which this row is responding
|
8
|
+
# :count - Number of data points returned (size of :results).
|
9
|
+
# :results - an Array of Hashes, each hash contains a set of
|
10
|
+
# data about one scanned Contract:
|
11
|
+
# :contract - a full description of the contract (details).
|
12
|
+
# :distance - Varies based on query.
|
13
|
+
# :benchmark - Varies based on query.
|
14
|
+
# :projection - Varies based on query.
|
15
|
+
# :legs - Describes combo legs when scan is returning EFP.
|
16
|
+
ScannerData = def_message [20, 3],
|
17
|
+
[:request_id, :int], # request id
|
18
|
+
[:count, :int]
|
19
|
+
class ScannerData
|
20
|
+
attr_accessor :results
|
21
|
+
using IBSupport # extended Array-Class from abstract_message
|
22
|
+
|
23
|
+
def load
|
24
|
+
super
|
25
|
+
|
26
|
+
@results = Array.new(@data[:count]) do |_|
|
27
|
+
{:rank => buffer.read_int,
|
28
|
+
:contract =>
|
29
|
+
Contract.build(
|
30
|
+
:con_id => buffer.read_int,
|
31
|
+
:symbol => buffer.read_string,
|
32
|
+
:sec_type => buffer.read_string,
|
33
|
+
:expiry => buffer.read_string,
|
34
|
+
:strike => buffer.read_decimal,
|
35
|
+
:right => buffer.read_string,
|
36
|
+
:exchange => buffer.read_string,
|
37
|
+
:currency => buffer.read_string,
|
38
|
+
:local_symbol => buffer.read_string,
|
39
|
+
:contract_detail =>
|
40
|
+
IB::ContractDetail.new(
|
41
|
+
:market_name => buffer.read_string,
|
42
|
+
:trading_class => buffer.read_string)),
|
43
|
+
:distance => buffer.read_string,
|
44
|
+
:benchmark => buffer.read_string,
|
45
|
+
:projection => buffer.read_string,
|
46
|
+
:legs => buffer.read_string,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end # ScannerData
|
51
|
+
|
52
|
+
end # module Incoming
|
53
|
+
end # module Messages
|
54
|
+
end # module IB
|
@@ -0,0 +1,268 @@
|
|
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
|
+
|
19
|
+
def the_data
|
20
|
+
@data.reject{|k,_| [:version, :ticker_id].include? k }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# The IB code seems to dispatch up to two wrapped objects for this message, a tickPrice
|
26
|
+
# and sometimes a tickSize, which seems to be identical to the TICK_SIZE object.
|
27
|
+
#
|
28
|
+
# Important note from
|
29
|
+
# http://chuckcaplan.com/twsapi/index.php/void%20tickPrice%28%29 :
|
30
|
+
#
|
31
|
+
# "The low you get is NOT the low for the day as you'd expect it
|
32
|
+
# to be. It appears IB calculates the low based on all
|
33
|
+
# transactions after 4pm the previous day. The most inaccurate
|
34
|
+
# results occur when the stock moves up in the 4-6pm aftermarket
|
35
|
+
# on the previous day and then gaps open upward in the
|
36
|
+
# morning. The low you receive from TWS can be easily be several
|
37
|
+
# points different from the actual 9:30am-4pm low for the day in
|
38
|
+
# cases like this. If you require a correct traded low for the
|
39
|
+
# day, you can't get it from the TWS API. One possible source to
|
40
|
+
# help build the right data would be to compare against what Yahoo
|
41
|
+
# lists on finance.yahoo.com/q?s=ticker under the "Day's Range"
|
42
|
+
# statistics (be careful here, because Yahoo will use anti-Denial
|
43
|
+
# of Service techniques to hang your connection if you try to
|
44
|
+
# request too many bytes in a short period of time from them). For
|
45
|
+
# most purposes, a good enough approach would start by replacing
|
46
|
+
# the TWS low for the day with Yahoo's day low when you first
|
47
|
+
# start watching a stock ticker; let's call this time T. Then,
|
48
|
+
# update your internal low if the bid or ask tick you receive is
|
49
|
+
# lower than that for the remainder of the day. You should check
|
50
|
+
# against Yahoo again at time T+20min to handle the occasional
|
51
|
+
# case where the stock set a new low for the day in between
|
52
|
+
# T-20min (the real time your original quote was from, taking into
|
53
|
+
# account the delay) and time T. After that you should have a
|
54
|
+
# correct enough low for the rest of the day as long as you keep
|
55
|
+
# updating based on the bid/ask. It could still get slightly off
|
56
|
+
# in a case where a short transaction setting a new low appears in
|
57
|
+
# between ticks of data that TWS sends you. The high is probably
|
58
|
+
# distorted in the same way the low is, which would throw your
|
59
|
+
# results off if the stock traded after-hours and gapped down. It
|
60
|
+
# should be corrected in a similar way as described above if this
|
61
|
+
# is important to you."
|
62
|
+
#
|
63
|
+
# IB then emits at most 2 events on eWrapper:
|
64
|
+
# tickPrice( tickerId, tickType, price, canAutoExecute)
|
65
|
+
# tickSize( tickerId, sizeTickType, size)
|
66
|
+
TickPrice = def_message [1, 6], AbstractTick,
|
67
|
+
[:ticker_id, :int],
|
68
|
+
[:tick_type, :int],
|
69
|
+
[:price, :float],
|
70
|
+
[:size, :int],
|
71
|
+
[:can_auto_execute, :int]
|
72
|
+
class TickPrice
|
73
|
+
def valid?
|
74
|
+
super && !price.zero?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
TickSize = def_message [2, 6], AbstractTick,
|
79
|
+
[:ticker_id, :int],
|
80
|
+
[:tick_type, :int],
|
81
|
+
[:size, :int]
|
82
|
+
|
83
|
+
TickGeneric = def_message [45, 6], AbstractTick,
|
84
|
+
[:ticker_id, :int],
|
85
|
+
[:tick_type, :int],
|
86
|
+
[:value, :decimal]
|
87
|
+
|
88
|
+
TickString = def_message [46, 6], AbstractTick,
|
89
|
+
[:ticker_id, :int],
|
90
|
+
[:tick_type, :int],
|
91
|
+
[:value, :string]
|
92
|
+
|
93
|
+
TickEFP = def_message [47, 6], AbstractTick,
|
94
|
+
[:ticker_id, :int],
|
95
|
+
[:tick_type, :int],
|
96
|
+
[:basis_points, :decimal],
|
97
|
+
[:formatted_basis_points, :string],
|
98
|
+
[:implied_futures_price, :decimal],
|
99
|
+
[:hold_days, :int],
|
100
|
+
[:dividend_impact, :decimal],
|
101
|
+
[:dividends_to_expiry, :decimal]
|
102
|
+
|
103
|
+
# This message is received when the market in an option or its underlier moves.
|
104
|
+
# TWS option model volatilities, prices, and deltas, along with the present
|
105
|
+
# value of dividends expected on that options underlier are received.
|
106
|
+
# TickOption message contains following @data:
|
107
|
+
# :ticker_id - Id that was specified previously in the call to reqMktData()
|
108
|
+
# :tick_type - Specifies the type of option computation (see TICK_TYPES).
|
109
|
+
# :implied_volatility - The implied volatility calculated by the TWS option
|
110
|
+
# modeler, using the specified :tick_type value.
|
111
|
+
# :delta - The option delta value.
|
112
|
+
# :option_price - The option price.
|
113
|
+
# :pv_dividend - The present value of dividends expected on the options underlier
|
114
|
+
# :gamma - The option gamma value.
|
115
|
+
# :vega - The option vega value.
|
116
|
+
# :theta - The option theta value.
|
117
|
+
# :under_price - The price of the underlying.
|
118
|
+
TickOptionComputation = TickOption =
|
119
|
+
def_message([21, 6], AbstractTick,
|
120
|
+
[:ticker_id, :int],
|
121
|
+
[:tick_type, :int],
|
122
|
+
# What is the "not yet computed" indicator:
|
123
|
+
[:implied_volatility, :decimal_limit_1], # -1 and below
|
124
|
+
[:delta, :decimal_limit_2], # -2 and below
|
125
|
+
[:option_price, :decimal_limit_1], # -1 -"-
|
126
|
+
[:pv_dividend, :decimal_limit_1], # -1 -"-
|
127
|
+
[:gamma, :decimal_limit_2], # -2 -"-
|
128
|
+
[:vega, :decimal_limit_2], # -2 -"-
|
129
|
+
[:theta, :decimal_limit_2], # -2 -"-
|
130
|
+
[:under_price, :decimal_limit_1]) do
|
131
|
+
|
132
|
+
"<TickOption #{type} for #{ticker_id}: underlying @ #{under_price}, "+
|
133
|
+
"option @ #{option_price}, IV #{implied_volatility}%, delta #{delta}, " +
|
134
|
+
"gamma #{gamma}, vega #{vega}, theta #{theta}, pv_dividend #{pv_dividend}>"
|
135
|
+
end
|
136
|
+
|
137
|
+
TickSnapshotEnd = def_message 57, [:ticker_id, :int]
|
138
|
+
=begin
|
139
|
+
def processTickByTickMsg(self, fields):
|
140
|
+
next(fields)
|
141
|
+
reqId = decode(int, fields)
|
142
|
+
tickType = decode(int, fields)
|
143
|
+
time = decode(int, fields)
|
144
|
+
|
145
|
+
if tickType == 0:
|
146
|
+
# None
|
147
|
+
pass
|
148
|
+
elif tickType == 1 or tickType == 2:
|
149
|
+
# Last or AllLast
|
150
|
+
price = decode(float, fields)
|
151
|
+
size = decode(int, fields)
|
152
|
+
mask = decode(int, fields)
|
153
|
+
class TickAttribLast(Object):
|
154
|
+
def __init__(self):
|
155
|
+
self.pastLimit = False
|
156
|
+
self.unreported = False
|
157
|
+
|
158
|
+
def __str__(self):
|
159
|
+
return "PastLimit: %d, Unreported: %d" % (self.pastLimit, self.unreported)
|
160
|
+
|
161
|
+
tickAttribLast = TickAttribLast()
|
162
|
+
tickAttribLast.pastLimit = mask & 1 != 0
|
163
|
+
tickAttribLast.unreported = mask & 2 != 0
|
164
|
+
exchange = decode(str, fields)
|
165
|
+
specialConditions = decode(str, fields)
|
166
|
+
|
167
|
+
self.wrapper.tickByTickAllLast(reqId, tickType, time, price, size, tickAttribLast,
|
168
|
+
exchange, specialConditions)
|
169
|
+
elif tickType == 3:
|
170
|
+
# BidAsk
|
171
|
+
bidPrice = decode(float, fields)
|
172
|
+
askPrice = decode(float, fields)
|
173
|
+
bidSize = decode(int, fields)
|
174
|
+
askSize = decode(int, fields)
|
175
|
+
mask = decode(int, fields)
|
176
|
+
class TickAttribBidAsk(Object):
|
177
|
+
def __init__(self):
|
178
|
+
self.bidPastLow = False
|
179
|
+
self.askPastHigh = False
|
180
|
+
|
181
|
+
def __str__(self):
|
182
|
+
return "BidPastLow: %d, AskPastHigh: %d" % (self.bidPastLow, self.askPastHigh)
|
183
|
+
|
184
|
+
|
185
|
+
tickAttribBidAsk = TickAttribBidAsk()
|
186
|
+
tickAttribBidAsk.bidPastLow = mask & 1 != 0
|
187
|
+
tickAttribBidAsk.askPastHigh = mask & 2 != 0
|
188
|
+
|
189
|
+
self.wrapper.tickByTickBidAsk(reqId, time, bidPrice, askPrice, bidSize,
|
190
|
+
askSize, tickAttribBidAsk)
|
191
|
+
elif tickType == 4:
|
192
|
+
# MidPoint
|
193
|
+
midPoint = decode(float, fields)
|
194
|
+
|
195
|
+
self.wrapper.tickByTickMidPoint(reqId, time, midPoint)
|
196
|
+
=end
|
197
|
+
TickByTick = def_message [99, 0], [:ticker_id, :int ],
|
198
|
+
[ :tick_type, :int],
|
199
|
+
[ :time, :int_date ]
|
200
|
+
|
201
|
+
## error messages: (10189) "Failed to request tick-by-tick data:Historical data request pacing violation"
|
202
|
+
#
|
203
|
+
class TickByTick
|
204
|
+
using IBSupport # extended Array-Class from abstract_message
|
205
|
+
|
206
|
+
def resolve_mask
|
207
|
+
@data[:mask].present? ? [ @data[:mask] & 1 , @data[:mask] & 2 ] : []
|
208
|
+
end
|
209
|
+
|
210
|
+
def load
|
211
|
+
super
|
212
|
+
case @data[:tick_type ]
|
213
|
+
when 0
|
214
|
+
# do nothing
|
215
|
+
when 1, 2 # Last, AllLast
|
216
|
+
load_map [ :price, :decimal ] ,
|
217
|
+
[ :size, :int ] ,
|
218
|
+
[ :mask, :int ] ,
|
219
|
+
[ :exchange, :string ],
|
220
|
+
[ :special_conditions, :string ]
|
221
|
+
when 3 # bid/ask
|
222
|
+
load_map [ :bid_price, :decimal ],
|
223
|
+
[ :ask_price, :decimal],
|
224
|
+
[ :bid_size, :int ],
|
225
|
+
[ :ask_size, :int] ,
|
226
|
+
[ :mask, :int ]
|
227
|
+
when 4
|
228
|
+
load_map [ :mid_point, :decimal ]
|
229
|
+
end
|
230
|
+
|
231
|
+
@out_labels = case @data[ :tick_tpye ]
|
232
|
+
when 1, 2
|
233
|
+
[ "PastLimit", "Unreported" ]
|
234
|
+
when 3
|
235
|
+
[ "BitPastLow", "BidPastHigh" ]
|
236
|
+
else
|
237
|
+
[]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
def to_human
|
241
|
+
"< TickByTick:" + case @data[ :tick_type ]
|
242
|
+
when 1,2
|
243
|
+
"(Last) #{size} @ #{price} [#{exchange}] "
|
244
|
+
when 3
|
245
|
+
"(Bid/Ask) #{bid_size} @ #{bid_price} / #{ask_size } @ #{ask_price} "
|
246
|
+
when 4
|
247
|
+
"(Midpoint) #{mid_point } "
|
248
|
+
else
|
249
|
+
""
|
250
|
+
end + @out_labels.zip(resolve_mask).join( "/" )
|
251
|
+
end
|
252
|
+
|
253
|
+
[:price, :size, :mask, :exchange, :specialConditions, :bid_price, :ask_price, :bid_size, :ask_size, :mid_point].each do |name|
|
254
|
+
define_method name do
|
255
|
+
@data[name]
|
256
|
+
end
|
257
|
+
end
|
258
|
+
# def method_missing method, *args
|
259
|
+
# if @data.keys.include? method
|
260
|
+
# @data[method]
|
261
|
+
# else
|
262
|
+
# error "method #{method} not known"
|
263
|
+
# end
|
264
|
+
# end
|
265
|
+
end
|
266
|
+
end # module Incoming
|
267
|
+
end # module Messages
|
268
|
+
end # module IB
|
@@ -0,0 +1,437 @@
|
|
1
|
+
require 'ib/messages/outgoing/abstract_message'
|
2
|
+
|
3
|
+
# TODO: Don't instantiate messages, use their classes as just namespace for .encode/decode
|
4
|
+
|
5
|
+
module IB
|
6
|
+
module Messages
|
7
|
+
|
8
|
+
# Outgoing IB messages (sent to TWS/Gateway)
|
9
|
+
module Outgoing
|
10
|
+
extend Messages # def_message macros
|
11
|
+
|
12
|
+
### Defining (short) Outgoing Message classes for IB:
|
13
|
+
|
14
|
+
## Empty messages (no data)
|
15
|
+
|
16
|
+
# Request the open orders that were placed from THIS client. Each open order
|
17
|
+
# will be fed back through the OpenOrder and OrderStatus messages ONCE.
|
18
|
+
# NB: Client with a client_id of 0 will also receive the TWS-owned open orders.
|
19
|
+
# These orders will be associated with the client and a new orderId will be
|
20
|
+
# generated. This association will persist over multiple API and TWS sessions.
|
21
|
+
RequestOpenOrders = def_message 5
|
22
|
+
|
23
|
+
# Request the open orders placed from all clients and also from TWS. Each open
|
24
|
+
# order will be fed back through the OpenOrder and OrderStatus messages ONCE.
|
25
|
+
# Note this does not re-bind those Orders to requesting Client!
|
26
|
+
# Use RequestAutoOpenOrders to request such re-binding.
|
27
|
+
RequestAllOpenOrders = def_message 16
|
28
|
+
|
29
|
+
# Request that newly created TWS orders be implicitly associated with this client.
|
30
|
+
# When a new TWS order is created, the order will be associated with this client
|
31
|
+
# and automatically fed back through the OpenOrder and OrderStatus messages.
|
32
|
+
# It is a 'continuous' request such that it gets turned 'on' when called with a
|
33
|
+
# TRUE auto_bind parameter. When it's called with FALSE auto_bind, new TWS orders
|
34
|
+
# will not bind to this client going forward. Note that TWS orders can only be
|
35
|
+
# bound to clients with a client_id of 0. TODO: how to properly test this?
|
36
|
+
# data = { :auto_bind => boolean }
|
37
|
+
RequestAutoOpenOrders = def_message 15, :auto_bind
|
38
|
+
|
39
|
+
# Requests an XML document that describes the valid parameters that a scanner
|
40
|
+
# subscription can have (for outgoing RequestScannerSubscription message).
|
41
|
+
RequestScannerParameters = def_message 24
|
42
|
+
|
43
|
+
RequestNewsArticle = def_message 84,
|
44
|
+
:request_id , # autogenerated
|
45
|
+
:provider_code,
|
46
|
+
:article_id,
|
47
|
+
:options # taglist
|
48
|
+
|
49
|
+
|
50
|
+
RequestNewsProviders = def_message 85 # no further parameters
|
51
|
+
RequestHistoricalNews = def_message 86,
|
52
|
+
:request_id , # autogenerated
|
53
|
+
:con_id,
|
54
|
+
:provider_code,
|
55
|
+
:start, # date
|
56
|
+
:total_results,
|
57
|
+
:options # taglist
|
58
|
+
|
59
|
+
|
60
|
+
CancelNewsBulletins = def_message 13
|
61
|
+
RequestCurrentTime = def_message 49
|
62
|
+
RequestGlobalCancel = def_message 58
|
63
|
+
|
64
|
+
## Data format is: @data = { :id => ticker_id}
|
65
|
+
CancelMarketData = def_message [2, 2]
|
66
|
+
CancelMarketDepth = def_message 11
|
67
|
+
CancelScannerSubscription = def_message 23
|
68
|
+
CancelHistoricalData = def_message 25
|
69
|
+
CancelRealTimeBars = def_message 51
|
70
|
+
|
71
|
+
## Data format is: @data = { :id => request_id }
|
72
|
+
CancelFundamentalData = def_message 53
|
73
|
+
CancelCalculateImpliedVolatility = CancelImpliedVolatility = def_message 56
|
74
|
+
CancelCalculateOptionPrice = CancelOptionPrice = def_message 57
|
75
|
+
|
76
|
+
## Data format is: @data ={ :id => local_id of order to cancel }
|
77
|
+
CancelOrder = def_message 4
|
78
|
+
|
79
|
+
# Request the next valid ID that can be used when placing an order. Responds with
|
80
|
+
# NextValidId message, and the id returned is that next valid Id for orders.
|
81
|
+
# That ID will reflect any autobinding that has occurred (which generates new
|
82
|
+
# IDs and increments the next valid ID therein).
|
83
|
+
# @data = { :number of ids requested => int } NB: :number option is ignored by TWS!
|
84
|
+
RequestIds = def_message 8, [:number, 1]
|
85
|
+
# data = { :all_messages => boolean }
|
86
|
+
RequestNewsBulletins = def_message 12, :all_messages
|
87
|
+
# data = { :log_level => int }
|
88
|
+
SetServerLoglevel = def_message 14, :log_level
|
89
|
+
# data = { :fa_data_type => int } 1 -> groups, 2 -> Profiles, 3 -> Account Aliases
|
90
|
+
RequestFA = def_message 18, :fa_data_type
|
91
|
+
# data = { :fa_data_type => int, :xml => String }
|
92
|
+
ReplaceFA = def_message 19, :fa_data_type, :xml
|
93
|
+
# data = { :market_data_type => int }
|
94
|
+
|
95
|
+
|
96
|
+
# data => { :id => request_id (int), :contract => Contract }
|
97
|
+
#
|
98
|
+
# Special case for options: "wildcards" in the Contract fields retrieve Option chains
|
99
|
+
# strike = 0 means all strikes
|
100
|
+
# right = "" meanns both call and put
|
101
|
+
# expiry = "" means all expiries
|
102
|
+
# expiry = "2013" means all expiries in 2013
|
103
|
+
# expiry = "201311" means all expiries in Nov 2013
|
104
|
+
# You'll get several ContractData (10) messages back if there is more than one match.
|
105
|
+
# When all the matches are delivered you'll get ContractDataEnd (52) message.
|
106
|
+
RequestContractDetails = RequestContractData =
|
107
|
+
def_message([9, 8], :request_id , # autogenerated
|
108
|
+
[:contract, :serialize_long, [:sec_id_type]])
|
109
|
+
|
110
|
+
# Requests security definition option parameters for viewing a contract's option chain
|
111
|
+
# request_id: The ID chosen for the request
|
112
|
+
# underlyingSymbol
|
113
|
+
# futFopExchange: The exchange on which the returned options are trading.
|
114
|
+
# Can be set to the empty string "" for all exchanges.
|
115
|
+
# underlyingSecType: The type of the underlying security, i.e. STK
|
116
|
+
# underlyingConId: the contract ID of the underlying security.
|
117
|
+
# con_id:
|
118
|
+
# Responses via Messages::Incoming::SecurityDefinitionOptionParameter
|
119
|
+
|
120
|
+
|
121
|
+
RequestSecurityDefinitionOptionParameters = ReqSecDefOptParams = RequestOptionChainDefinition = def_message [78,0],
|
122
|
+
:request_id, # autogenerated if not specified
|
123
|
+
:symbol, # underlyingSymbol
|
124
|
+
[:exchange, ""], # futOptExchange
|
125
|
+
:sec_type, # underlyingSecType
|
126
|
+
:con_id # underlyingConId (required)
|
127
|
+
|
128
|
+
# data = { :id => ticker_id (int), :contract => Contract, :num_rows => int }
|
129
|
+
|
130
|
+
|
131
|
+
# Requests venues for which market data is returned to updateMktDepthL2
|
132
|
+
# returns MarketDepthExchanges-Message
|
133
|
+
#
|
134
|
+
RequestMarketDepthExchanges = # requires ServerVersion >= 112
|
135
|
+
def_message 82
|
136
|
+
|
137
|
+
|
138
|
+
## actual Version supported is: 137
|
139
|
+
## changes: MIN_SERVER_VER_SMART_DEPTH: 146 --> insert 'is_smarth_depth' after 'num_rows'
|
140
|
+
## then: 'is_smart_depth' (bool) has to be specified in CancelMarketDepth, too
|
141
|
+
#
|
142
|
+
RequestMarketDepth = def_message([10, 5],
|
143
|
+
:request_id, # autogenerated if not specified
|
144
|
+
[:contract, :serialize_supershort ],
|
145
|
+
:num_rows,
|
146
|
+
"") # mktDataOptionsStr. ## not supported by api
|
147
|
+
|
148
|
+
# When this message is sent, TWS responds with ExecutionData messages, each
|
149
|
+
# containing the execution report that meets the specified criteria.
|
150
|
+
# @data={:id => int: :request_id,
|
151
|
+
# :client_id => int: Filter the results based on the clientId.
|
152
|
+
# :account => Filter the results based on based on account code.
|
153
|
+
# Note: this is only relevant for Financial Advisor accts.
|
154
|
+
# :sec_type => Filter the results based on the order security type.
|
155
|
+
# :time => Filter the results based on execution reports received
|
156
|
+
# after the specified time - format "yyyymmdd-hh:mm:ss"
|
157
|
+
# :symbol => Filter the results based on the order symbol.
|
158
|
+
# :exchange => Filter the results based on the order exchange
|
159
|
+
# :side => Filter the results based on the order action: BUY/SELL/SSHORT
|
160
|
+
RequestExecutions = def_message([7, 3],
|
161
|
+
:request_id, # autogenerated if not specified
|
162
|
+
:client_id,
|
163
|
+
:account,
|
164
|
+
:time, # Format "yyyymmdd-hh:mm:ss"
|
165
|
+
:symbol,
|
166
|
+
:sec_type,
|
167
|
+
:exchange,
|
168
|
+
:side)
|
169
|
+
|
170
|
+
# data = { :id => ticker_id (int),
|
171
|
+
# :contract => IB::Contract,
|
172
|
+
# :exercise_action => int, 1 = exercise, 2 = lapse
|
173
|
+
# :exercise_quantity => int, The number of contracts to be exercised
|
174
|
+
# :account => string,
|
175
|
+
# :override => int: Specifies whether your setting will override the
|
176
|
+
# system's natural action. For example, if your action
|
177
|
+
# is "exercise" and the option is not in-the-money, by
|
178
|
+
# natural action the option would not exercise. If you
|
179
|
+
# have override set to "yes" the natural action would be
|
180
|
+
# overridden and the out-of-the money option would be
|
181
|
+
# exercised. Values are:
|
182
|
+
# - 0 = do not override
|
183
|
+
# - 1 = override
|
184
|
+
ExerciseOptions = def_message([ 21, 2 ],
|
185
|
+
# :request_id, # id -> required # todo : TEST
|
186
|
+
[:contract, :serialize_short],
|
187
|
+
:exercise_action,
|
188
|
+
:exercise_quantity,
|
189
|
+
:account,
|
190
|
+
:override)
|
191
|
+
|
192
|
+
|
193
|
+
# The API can receive frozen market data from Trader Workstation. Frozen market
|
194
|
+
# data is the last data recorded in our system. During normal trading hours,
|
195
|
+
# the API receives real-time market data. If you use this function, you are
|
196
|
+
# telling TWS to automatically switch to frozen market data AFTER the close.
|
197
|
+
# Then, before the opening of the next trading day, market data will automatically
|
198
|
+
# switch back to real-time market data.
|
199
|
+
# :market_data_type = 1 (:real_time) for real-time streaming, 2 (:frozen) for frozen market data
|
200
|
+
# = 3 (:delayed) for delayed streaming , 4 (:frozen_delayed) for frozen delayed
|
201
|
+
RequestMarketDataType =
|
202
|
+
def_message 59, [:market_data_type,
|
203
|
+
lambda { |type| MARKET_DATA_TYPES.invert[type] || type }, []]
|
204
|
+
|
205
|
+
# Send this message to receive Reuters global fundamental data. There must be
|
206
|
+
# a subscription to Reuters Fundamental set up in Account Management before
|
207
|
+
# you can receive this data.
|
208
|
+
# data = { :id => int: :request_id,
|
209
|
+
# :contract => Contract,
|
210
|
+
# :report_type => String: one of the following:
|
211
|
+
# 'estimates' - Estimates
|
212
|
+
# 'finstat' - Financial statements
|
213
|
+
# 'snapshot' - Summary }a
|
214
|
+
# ReportsFinSummary Financial summary
|
215
|
+
#ReportsOwnership Company's ownership (Can be large in size)
|
216
|
+
#ReportSnapshot Company's financial overview
|
217
|
+
#ReportsFinStatements Financial Statements
|
218
|
+
#RESC Analyst Estimates
|
219
|
+
#CalendarReport Company's calendar
|
220
|
+
RequestFundamentalData =
|
221
|
+
def_message([52,2],
|
222
|
+
:request_id, # autogenerated if not specified
|
223
|
+
[:contract, :serialize, :primary_exchange],
|
224
|
+
:report_type,
|
225
|
+
"" )
|
226
|
+
|
227
|
+
# Returns the timestamp of earliest available historical data for a contract and data type.
|
228
|
+
# :what_to_show: type of data for head timestamp - "BID", "ASK", "TRADES", etc
|
229
|
+
# :use_rth : use regular trading hours only, 1 for yes or 0 for no
|
230
|
+
# format_data : set to 2 to obtain it like system time format in second ---> don't change
|
231
|
+
RequestHeadTimeStamp =
|
232
|
+
def_message( [87,0], :request_id, # autogenerated
|
233
|
+
[:contract, :serialize_short, [:primary_exchange,:include_expired] ],
|
234
|
+
[:use_rth, 1 ],
|
235
|
+
[:what_to_show, 'Trades' ],
|
236
|
+
[:format_date, 2 ] ) ## don't change!
|
237
|
+
|
238
|
+
CancelHeadTimeStamp =
|
239
|
+
def_message [90,0 ] # , :(request_)id #required
|
240
|
+
|
241
|
+
|
242
|
+
|
243
|
+
RequestHistogramData =
|
244
|
+
def_message( [88, 0], :request_id, # autogenerated
|
245
|
+
[:contract, :serialize_short, [:primary_exchange,:include_expired] ],
|
246
|
+
[:use_rth, 1 ],
|
247
|
+
[:time_period ] )
|
248
|
+
|
249
|
+
CancelHistogramData =
|
250
|
+
def_message [89,0 ] # , :(request_)id required
|
251
|
+
|
252
|
+
RequestCalculateImpliedVolatility = CalculateImpliedVolatility =
|
253
|
+
RequestImpliedVolatility =
|
254
|
+
def_message([ 54,3 ],:request_id, # autogenerated
|
255
|
+
[:contract, :serialize_short, :primary_exchange],
|
256
|
+
:option_price,
|
257
|
+
:under_price,
|
258
|
+
[:implied_volatility_options_count, 0],
|
259
|
+
[:implied_volatility_options_conditions, ''])
|
260
|
+
|
261
|
+
# data = { :request_id => int, :contract => Contract,
|
262
|
+
# :volatility => double, :under_price => double }
|
263
|
+
RequestCalculateOptionPrice = CalculateOptionPrice = RequestOptionPrice =
|
264
|
+
def_message([ 55, 3], :request_id, #autogenerated if not specified
|
265
|
+
[:contract, :serialize_short, :primary_exchange],
|
266
|
+
:volatility,
|
267
|
+
:under_price,
|
268
|
+
[:implied_volatility_options_count, 0],
|
269
|
+
[:implied_volatility_options_conditions, ''])
|
270
|
+
|
271
|
+
# Start receiving market scanner results through the ScannerData messages.
|
272
|
+
# @data = { :id => ticker_id (int),
|
273
|
+
# :number_of_rows => int: number of rows of data to return for a query.
|
274
|
+
# :instrument => The instrument type for the scan. Values include
|
275
|
+
# 'STK', - US stocks
|
276
|
+
# 'STOCK.HK' - Asian stocks
|
277
|
+
# 'STOCK.EU' - European stocks
|
278
|
+
# :location_code => Legal Values include:
|
279
|
+
# - STK.US - US stocks
|
280
|
+
# - STK.US.MAJOR - US stocks (without pink sheet)
|
281
|
+
# - STK.US.MINOR - US stocks (only pink sheet)
|
282
|
+
# - STK.HK.SEHK - Hong Kong stocks
|
283
|
+
# - STK.HK.ASX - Australian Stocks
|
284
|
+
# - STK.EU - European stocks
|
285
|
+
# :scan_code => The type of the scan, such as HIGH_OPT_VOLUME_PUT_CALL_RATIO.
|
286
|
+
# :above_price => double: Only contracts with a price above this value.
|
287
|
+
# :below_price => double: Only contracts with a price below this value.
|
288
|
+
# :above_volume => int: Only contracts with a volume above this value.
|
289
|
+
# :market_cap_above => double: Only contracts with a market cap above this
|
290
|
+
# :market_cap_below => double: Only contracts with a market cap below this value.
|
291
|
+
# :moody_rating_above => Only contracts with a Moody rating above this value.
|
292
|
+
# :moody_rating_below => Only contracts with a Moody rating below this value.
|
293
|
+
# :sp_rating_above => Only contracts with an S&P rating above this value.
|
294
|
+
# :sp_rating_below => Only contracts with an S&P rating below this value.
|
295
|
+
# :maturity_date_above => Only contracts with a maturity date later than this
|
296
|
+
# :maturity_date_below => Only contracts with a maturity date earlier than this
|
297
|
+
# :coupon_rate_above => double: Only contracts with a coupon rate above this
|
298
|
+
# :coupon_rate_below => double: Only contracts with a coupon rate below this
|
299
|
+
# :exclude_convertible => Exclude convertible bonds.
|
300
|
+
# :scanner_setting_pairs => Used with the scan_code to help further narrow your query.
|
301
|
+
# Scanner Setting Pairs are delimited by slashes, making
|
302
|
+
# this parameter open ended. Example is "Annual,true" -
|
303
|
+
# when used with 'Top Option Implied Vol % Gainers' scan
|
304
|
+
# would return annualized volatilities.
|
305
|
+
# :average_option_volume_above => int: Only contracts with average volume above this
|
306
|
+
# :stock_type_filter => Valid values are:
|
307
|
+
# 'ALL' (excludes nothing)
|
308
|
+
# 'STOCK' (excludes ETFs)
|
309
|
+
# 'ETF' (includes ETFs) }
|
310
|
+
# ------------
|
311
|
+
# To learn all valid parameter values that a scanner subscription can have,
|
312
|
+
# first subscribe to ScannerParameters and send RequestScannerParameters message.
|
313
|
+
# Available scanner parameters values will be listed in received XML document.
|
314
|
+
RequestScannerSubscription =
|
315
|
+
def_message([22, 3], :request_id ,
|
316
|
+
[:number_of_rows, -1], # was: EOL,
|
317
|
+
:instrument,
|
318
|
+
:location_code,
|
319
|
+
:scan_code,
|
320
|
+
:above_price,
|
321
|
+
:below_price,
|
322
|
+
:above_volume,
|
323
|
+
:market_cap_above,
|
324
|
+
:market_cap_below,
|
325
|
+
:moody_rating_above,
|
326
|
+
:moody_rating_below,
|
327
|
+
:sp_rating_above,
|
328
|
+
:sp_rating_below,
|
329
|
+
:maturity_date_above,
|
330
|
+
:maturity_date_below,
|
331
|
+
:coupon_rate_above,
|
332
|
+
:coupon_rate_below,
|
333
|
+
:exclude_convertible,
|
334
|
+
:average_option_volume_above, # ?
|
335
|
+
:scanner_setting_pairs,
|
336
|
+
:stock_type_filter)
|
337
|
+
|
338
|
+
|
339
|
+
|
340
|
+
require 'ib/messages/outgoing/place_order'
|
341
|
+
require 'ib/messages/outgoing/bar_requests'
|
342
|
+
require 'ib/messages/outgoing/account_requests'
|
343
|
+
require 'ib/messages/outgoing/request_marketdata'
|
344
|
+
require 'ib/messages/outgoing/request_tick_data'
|
345
|
+
|
346
|
+
end # module Outgoing
|
347
|
+
end # module Messages
|
348
|
+
end # module IB
|
349
|
+
|
350
|
+
__END__
|
351
|
+
## python: message.py
|
352
|
+
REQ_MKT_DATA = 1
|
353
|
+
CANCEL_MKT_DATA = 2
|
354
|
+
PLACE_ORDER = 3
|
355
|
+
CANCEL_ORDER = 4
|
356
|
+
REQ_OPEN_ORDERS = 5
|
357
|
+
REQ_ACCT_DATA = 6
|
358
|
+
REQ_EXECUTIONS = 7
|
359
|
+
REQ_IDS = 8
|
360
|
+
REQ_CONTRACT_DATA = 9
|
361
|
+
REQ_MKT_DEPTH = 10
|
362
|
+
CANCEL_MKT_DEPTH = 11
|
363
|
+
REQ_NEWS_BULLETINS = 12
|
364
|
+
CANCEL_NEWS_BULLETINS = 13
|
365
|
+
SET_SERVER_LOGLEVEL = 14
|
366
|
+
REQ_AUTO_OPEN_ORDERS = 15
|
367
|
+
REQ_ALL_OPEN_ORDERS = 16
|
368
|
+
REQ_MANAGED_ACCTS = 17
|
369
|
+
REQ_FA = 18
|
370
|
+
REPLACE_FA = 19
|
371
|
+
REQ_HISTORICAL_DATA = 20
|
372
|
+
EXERCISE_OPTIONS = 21
|
373
|
+
REQ_SCANNER_SUBSCRIPTION = 22
|
374
|
+
CANCEL_SCANNER_SUBSCRIPTION = 23
|
375
|
+
REQ_SCANNER_PARAMETERS = 24
|
376
|
+
CANCEL_HISTORICAL_DATA = 25
|
377
|
+
REQ_CURRENT_TIME = 49
|
378
|
+
REQ_REAL_TIME_BARS = 50
|
379
|
+
CANCEL_REAL_TIME_BARS = 51
|
380
|
+
REQ_FUNDAMENTAL_DATA = 52
|
381
|
+
CANCEL_FUNDAMENTAL_DATA = 53
|
382
|
+
REQ_CALC_IMPLIED_VOLAT = 54
|
383
|
+
REQ_CALC_OPTION_PRICE = 55
|
384
|
+
CANCEL_CALC_IMPLIED_VOLAT = 56
|
385
|
+
CANCEL_CALC_OPTION_PRICE = 57
|
386
|
+
REQ_GLOBAL_CANCEL = 58
|
387
|
+
REQ_MARKET_DATA_TYPE = 59 --> supported by ib-ruby 0.94
|
388
|
+
|
389
|
+
REQ_POSITIONS = 61 supported now
|
390
|
+
REQ_ACCOUNT_SUMMARY = 62 supported now
|
391
|
+
|
392
|
+
CANCEL_ACCOUNT_SUMMARY = 63 supported now
|
393
|
+
|
394
|
+
CANCEL_POSITIONS = 64 supported now
|
395
|
+
VERIFY_REQUEST = 65
|
396
|
+
VERIFY_MESSAGE = 66
|
397
|
+
QUERY_DISPLAY_GROUPS = 67
|
398
|
+
SUBSCRIBE_TO_GROUP_EVENTS = 68
|
399
|
+
UPDATE_DISPLAY_GROUP = 69
|
400
|
+
UNSUBSCRIBE_FROM_GROUP_EVENTS = 70
|
401
|
+
START_API = 71
|
402
|
+
VERIFY_AND_AUTH_REQUEST = 72
|
403
|
+
VERIFY_AND_AUTH_MESSAGE = 73
|
404
|
+
REQ_POSITIONS_MULTI = 74 supported now
|
405
|
+
CANCEL_POSITIONS_MULTI = 75 supported now
|
406
|
+
|
407
|
+
REQ_ACCOUNT_UPDATES_MULTI = 76 supported now
|
408
|
+
|
409
|
+
CANCEL_ACCOUNT_UPDATES_MULTI = 77 supported now
|
410
|
+
|
411
|
+
REQ_SEC_DEF_OPT_PARAMS = 78 supported now
|
412
|
+
REQ_SOFT_DOLLAR_TIERS = 79
|
413
|
+
REQ_FAMILY_CODES = 80
|
414
|
+
REQ_MATCHING_SYMBOLS = 81
|
415
|
+
REQ_MKT_DEPTH_EXCHANGES = 82
|
416
|
+
REQ_SMART_COMPONENTS = 83
|
417
|
+
REQ_NEWS_ARTICLE = 84 in preparation
|
418
|
+
REQ_NEWS_PROVIDERS = 85 in preparatino
|
419
|
+
REQ_HISTORICAL_NEWS = 86 in preparation
|
420
|
+
|
421
|
+
REQ_HEAD_TIMESTAMP = 87 supported now
|
422
|
+
|
423
|
+
REQ_HISTOGRAM_DATA = 88 supported now
|
424
|
+
|
425
|
+
CANCEL_HISTOGRAM_DATA = 89 supported now
|
426
|
+
|
427
|
+
CANCEL_HEAD_TIMESTAMP = 90 supported now
|
428
|
+
|
429
|
+
REQ_MARKET_RULE = 91
|
430
|
+
REQ_PNL = 92
|
431
|
+
CANCEL_PNL = 93
|
432
|
+
REQ_PNL_SINGLE = 94
|
433
|
+
CANCEL_PNL_SINGLE = 95
|
434
|
+
REQ_HISTORICAL_TICKS = 96
|
435
|
+
REQ_TICK_BY_TICK_DATA = 97
|
436
|
+
CANCEL_TICK_BY_TICK_DATA = 98
|
437
|
+
|