ib-api 972.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|