ib-ruby 0.5.19 → 0.5.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +4 -0
- data/TODO +0 -3
- data/VERSION +1 -1
- data/bin/contract_details +3 -3
- data/bin/depth_of_market +1 -1
- data/bin/historic_data +5 -8
- data/bin/market_data +2 -2
- data/bin/option_data +2 -2
- data/lib/ib-ruby/connection.rb +1 -9
- data/lib/ib-ruby/extensions.rb +8 -0
- data/lib/ib-ruby/messages/abstract_message.rb +89 -0
- data/lib/ib-ruby/messages/incoming.rb +415 -487
- data/lib/ib-ruby/messages/outgoing.rb +241 -305
- data/lib/ib-ruby/models/bar.rb +3 -3
- data/lib/ib-ruby/models/contract/bag.rb +1 -5
- data/lib/ib-ruby/models/contract.rb +50 -33
- data/lib/ib-ruby/models/execution.rb +6 -3
- data/lib/ib-ruby/models/order.rb +7 -5
- data/lib/ib-ruby/socket.rb +13 -0
- data/lib/ib-ruby/symbols/forex.rb +7 -14
- data/lib/ib-ruby/symbols/futures.rb +16 -20
- data/lib/ib-ruby/symbols/options.rb +6 -4
- data/lib/ib-ruby/symbols/stocks.rb +1 -1
- data/lib/ib-ruby.rb +1 -0
- data/spec/README.md +34 -0
- data/spec/ib-ruby/connection_spec.rb +4 -4
- data/spec/ib-ruby/messages/incoming_spec.rb +50 -0
- data/spec/ib-ruby/messages/outgoing_spec.rb +32 -0
- data/spec/ib-ruby/models/contract_spec.rb +27 -25
- data/spec/ib-ruby/models/order_spec.rb +56 -23
- data/spec/integration/account_info_spec.rb +85 -0
- data/spec/integration/contract_info_spec.rb +209 -0
- data/spec/integration/depth_data_spec.rb +46 -0
- data/spec/integration/historic_data_spec.rb +82 -0
- data/spec/integration/market_data_spec.rb +97 -0
- data/spec/integration/option_data_spec.rb +96 -0
- data/spec/integration/orders/execution_spec.rb +135 -0
- data/spec/{ib-ruby/messages → integration/orders}/open_order +9 -205
- data/spec/integration/orders/placement_spec.rb +150 -0
- data/spec/integration/orders/valid_ids_spec.rb +84 -0
- data/spec/integration_helper.rb +110 -0
- data/spec/message_helper.rb +13 -18
- data/spec/spec_helper.rb +35 -26
- metadata +33 -17
- data/spec/ib-ruby/messages/README.md +0 -16
- data/spec/ib-ruby/messages/account_info_spec.rb +0 -84
- data/spec/ib-ruby/messages/just_connect_spec.rb +0 -33
- data/spec/ib-ruby/messages/market_data_spec.rb +0 -92
- data/spec/ib-ruby/messages/orders_spec.rb +0 -219
- data/spec/ib-ruby_spec.rb +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ib-ruby/messages/abstract_message'
|
2
|
+
|
1
3
|
# EClientSocket.java uses sendMax() rather than send() for a number of these.
|
2
4
|
# It sends an EOL rather than a number if the value == Integer.MAX_VALUE (or Double.MAX_VALUE).
|
3
5
|
# These fields are initialized to this MAX_VALUE.
|
@@ -8,29 +10,15 @@
|
|
8
10
|
module IB
|
9
11
|
module Messages
|
10
12
|
module Outgoing
|
13
|
+
extend Messages # def_message macros
|
11
14
|
|
12
|
-
class AbstractMessage
|
13
|
-
# Class methods
|
14
|
-
def self.message_id
|
15
|
-
@message_id
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.version
|
19
|
-
@version
|
20
|
-
end
|
21
|
-
|
22
|
-
attr_reader :created_at, :data
|
15
|
+
class AbstractMessage < IB::Messages::AbstractMessage
|
23
16
|
|
24
|
-
|
25
|
-
def initialize(data = {})
|
17
|
+
def initialize data={}
|
26
18
|
@data = data
|
27
19
|
@created_at = Time.now
|
28
20
|
end
|
29
21
|
|
30
|
-
def to_human
|
31
|
-
self.inspect
|
32
|
-
end
|
33
|
-
|
34
22
|
# This causes the message to send itself over the server socket in server[:socket].
|
35
23
|
# "server" is the @server instance variable from the IB object.
|
36
24
|
# You can also use this to e.g. get the server version number.
|
@@ -46,31 +34,37 @@ module IB
|
|
46
34
|
datum = "1" if datum == true
|
47
35
|
datum = "0" if datum == false
|
48
36
|
|
37
|
+
#p datum.to_s + EOL
|
49
38
|
server[:socket].syswrite(datum.to_s + EOL)
|
50
39
|
end
|
51
40
|
end
|
52
41
|
|
53
42
|
# At minimum, Outgoing message contains message_id and version.
|
54
43
|
# Most messages also contain (ticker, request or order) :id.
|
44
|
+
# Then, content of @data Hash is encoded per instructions in data_map.
|
55
45
|
def encode
|
56
46
|
[self.class.message_id,
|
57
47
|
self.class.version,
|
58
|
-
@data[:id] || []]
|
48
|
+
@data[:id] || @data[:ticker_id] || @data[:request_id]|| @data[:order_id] || [],
|
49
|
+
self.class.data_map.map do |(field, default_method, args)|
|
50
|
+
case
|
51
|
+
when default_method.nil?
|
52
|
+
@data[field]
|
53
|
+
|
54
|
+
when default_method.is_a?(Symbol) # method name with args
|
55
|
+
@data[field].send default_method, *args
|
56
|
+
|
57
|
+
when default_method.respond_to?(:call) # callable with args
|
58
|
+
default_method.call @data[field], *args
|
59
|
+
|
60
|
+
else # default
|
61
|
+
@data[field].nil? ? default_method : @data[field] # may be false still
|
62
|
+
end
|
63
|
+
end
|
64
|
+
].flatten
|
59
65
|
end
|
60
66
|
end # AbstractMessage
|
61
67
|
|
62
|
-
# Macro that defines short message classes using a one-liner
|
63
|
-
def self.def_message message_id, version=1, *keys
|
64
|
-
Class.new(AbstractMessage) do
|
65
|
-
@message_id = message_id
|
66
|
-
@version = version
|
67
|
-
|
68
|
-
define_method :encode do
|
69
|
-
[super(), keys.map { |key| @data[key] }]
|
70
|
-
end unless keys.empty?
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
68
|
### Defining (short) Outgoing Message classes for IB:
|
75
69
|
|
76
70
|
## Empty messages (no data)
|
@@ -104,35 +98,151 @@ module IB
|
|
104
98
|
|
105
99
|
## Data format is: @data = { :id => request_id }
|
106
100
|
CancelFundamentalData = def_message 53
|
107
|
-
CancelImpliedVolatility = def_message
|
108
|
-
|
109
|
-
CancelOptionPrice = def_message 57
|
110
|
-
CancelCalculateOptionPrice = CancelOptionPrice
|
101
|
+
CancelCalculateImpliedVolatility = CancelImpliedVolatility = def_message(56)
|
102
|
+
CancelCalculateOptionPrice = CancelOptionPrice = def_message(57)
|
111
103
|
|
112
|
-
## Data format is: @data ={ :id =>
|
104
|
+
## Data format is: @data ={ :id => order_id to cancel }
|
113
105
|
CancelOrder = def_message 4
|
114
106
|
|
115
|
-
## These messages contain just one or two
|
116
|
-
|
117
|
-
|
107
|
+
## These messages contain just one or two extra fields:
|
108
|
+
|
109
|
+
# Request the next valid ID that can be used when placing an order. Responds with
|
110
|
+
# NextValidId message, and the id returned is that next valid Id for orders.
|
111
|
+
# That ID will reflect any autobinding that has occurred (which generates new
|
112
|
+
# IDs and increments the next valid ID therein).
|
113
|
+
# @data = { :number of ids requested => int } NB: :number option is ignored by TWS!
|
114
|
+
RequestIds = def_message 8, [:number, 1]
|
118
115
|
# data = { :all_messages => boolean }
|
119
|
-
RequestNewsBulletins = def_message 12,
|
116
|
+
RequestNewsBulletins = def_message 12, :all_messages
|
120
117
|
# data = { :log_level => int }
|
121
|
-
SetServerLoglevel = def_message 14,
|
118
|
+
SetServerLoglevel = def_message 14, :log_level
|
122
119
|
# data = { :auto_bind => boolean }
|
123
|
-
RequestAutoOpenOrders = def_message 15,
|
120
|
+
RequestAutoOpenOrders = def_message 15, :auto_bind
|
124
121
|
# data = { :fa_data_type => int }
|
125
|
-
RequestFA = def_message 18,
|
122
|
+
RequestFA = def_message 18, :fa_data_type
|
126
123
|
# data = { :fa_data_type => int, :xml => String }
|
127
|
-
ReplaceFA = def_message 19,
|
124
|
+
ReplaceFA = def_message 19, :fa_data_type, :xml
|
125
|
+
|
128
126
|
# @data = { :subscribe => boolean,
|
129
127
|
# :account_code => Advisor accounts only. Empty ('') for a standard account. }
|
130
|
-
RequestAccountData = def_message
|
131
|
-
|
128
|
+
RequestAccountUpdates = RequestAccountData = def_message([6, 2],
|
129
|
+
[:subscribe, true],
|
130
|
+
:account_code)
|
132
131
|
|
132
|
+
# data => { :id => request_id (int), :contract => Contract }
|
133
|
+
RequestContractDetails = RequestContractData =
|
134
|
+
def_message([9, 6],
|
135
|
+
[:contract, :serialize_short, [:con_id, :include_expired, :sec_id]])
|
136
|
+
|
137
|
+
# data = { :id => ticker_id (int), :contract => Contract, :num_rows => int }
|
138
|
+
RequestMarketDepth = def_message([10, 3],
|
139
|
+
[:contract, :serialize_short, []],
|
140
|
+
:num_rows)
|
133
141
|
|
134
142
|
### Defining (complex) Outgoing Message classes for IB:
|
135
143
|
|
144
|
+
# When this message is sent, TWS responds with ExecutionData messages, each
|
145
|
+
# containing the execution report that meets the specified criteria.
|
146
|
+
# @data={:id => int: :request_id,
|
147
|
+
# :client_id => int: Filter the results based on the clientId.
|
148
|
+
# :acct_code => Filter the results based on based on account code.
|
149
|
+
# Note: this is only relevant for Financial Advisor accts.
|
150
|
+
# :sec_type => Filter the results based on the order security type.
|
151
|
+
# :time => Filter the results based on execution reports received
|
152
|
+
# after the specified time - format "yyyymmdd-hh:mm:ss"
|
153
|
+
# :symbol => Filter the results based on the order symbol.
|
154
|
+
# :exchange => Filter the results based on the order exchange
|
155
|
+
# :side => Filter the results based on the order action: BUY/SELL/SSHORT
|
156
|
+
RequestExecutions = def_message([7, 3],
|
157
|
+
:client_id,
|
158
|
+
:acct_code,
|
159
|
+
:time, # Format "yyyymmdd-hh:mm:ss"
|
160
|
+
:symbol,
|
161
|
+
:sec_type,
|
162
|
+
:exchange,
|
163
|
+
:side)
|
164
|
+
|
165
|
+
# data = { :id => ticker_id (int),
|
166
|
+
# :contract => Contract,
|
167
|
+
# :exercise_action => int, 1 = exercise, 2 = lapse
|
168
|
+
# :exercise_quantity => int, The number of contracts to be exercised
|
169
|
+
# :account => string,
|
170
|
+
# :override => int: Specifies whether your setting will override the
|
171
|
+
# system's natural action. For example, if your action
|
172
|
+
# is "exercise" and the option is not in-the-money, by
|
173
|
+
# natural action the option would not exercise. If you
|
174
|
+
# have override set to "yes" the natural action would be
|
175
|
+
# overridden and the out-of-the money option would be
|
176
|
+
# exercised. Values are:
|
177
|
+
# � 0 = do not override
|
178
|
+
# � 1 = override
|
179
|
+
ExerciseOptions = def_message(21,
|
180
|
+
[:contract, :serialize_short],
|
181
|
+
:exercise_action,
|
182
|
+
:exercise_quantity,
|
183
|
+
:account,
|
184
|
+
:override)
|
185
|
+
|
186
|
+
# @data={:id => int: ticker_id - Must be a unique value. When the market data
|
187
|
+
# returns, it will be identified by this tag,
|
188
|
+
# :contract => Models::Contract, requested contract.
|
189
|
+
# :tick_list => String: comma delimited list of requested tick groups:
|
190
|
+
# Group ID - Description - Requested Tick Types
|
191
|
+
# 100 - Option Volume (currently for stocks) - 29, 30
|
192
|
+
# 101 - Option Open Interest (currently for stocks) - 27, 28
|
193
|
+
# 104 - Historical Volatility (currently for stocks) - 23
|
194
|
+
# 106 - Option Implied Volatility (currently for stocks) - 24
|
195
|
+
# 162 - Index Future Premium - 31
|
196
|
+
# 165 - Miscellaneous Stats - 15, 16, 17, 18, 19, 20, 21
|
197
|
+
# 221 - Mark Price (used in TWS P&L computations) - 37
|
198
|
+
# 225 - Auction values (volume, price and imbalance) - 34, 35, 36
|
199
|
+
# 233 - RTVolume - 48
|
200
|
+
# 236 - Shortable - 46
|
201
|
+
# 256 - Inventory - ?
|
202
|
+
# 258 - Fundamental Ratios - 47
|
203
|
+
# 411 - Realtime Historical Volatility - 58
|
204
|
+
# :snapshot => bool: Check to return a single snapshot of market data and
|
205
|
+
# have the market data subscription canceled. Do not enter any
|
206
|
+
# :tick_list values if you use snapshot. }
|
207
|
+
RequestMarketData =
|
208
|
+
def_message([1, 9],
|
209
|
+
[:contract, :serialize_long, [:con_id]],
|
210
|
+
[:contract, :serialize_legs, []],
|
211
|
+
[:contract, :serialize_under_comp, []],
|
212
|
+
[:tick_list, lambda do |tick_list|
|
213
|
+
tick_list.is_a?(Array) ? tick_list.join(',') : (tick_list || '')
|
214
|
+
end, []],
|
215
|
+
[:snapshot, false])
|
216
|
+
|
217
|
+
# Send this message to receive Reuters global fundamental data. There must be
|
218
|
+
# a subscription to Reuters Fundamental set up in Account Management before
|
219
|
+
# you can receive this data.
|
220
|
+
# data = { :id => int: :request_id,
|
221
|
+
# :contract => Contract,
|
222
|
+
# :report_type => String: one of the following:
|
223
|
+
# 'Estimates', 'Financial Statements', 'Summary' }
|
224
|
+
RequestFundamentalData =
|
225
|
+
def_message(52,
|
226
|
+
[:contract, :serialize, [:primary_exchange]],
|
227
|
+
:report_type)
|
228
|
+
|
229
|
+
# data = { :request_id => int, :contract => Contract,
|
230
|
+
# :option_price => double, :under_price => double }
|
231
|
+
RequestCalculateImpliedVolatility = CalculateImpliedVolatility =
|
232
|
+
RequestImpliedVolatility =
|
233
|
+
def_message(54,
|
234
|
+
[:contract, :serialize_long, [:con_id]],
|
235
|
+
:option_price,
|
236
|
+
:under_price)
|
237
|
+
|
238
|
+
# data = { :request_id => int, :contract => Contract,
|
239
|
+
# :volatility => double, :under_price => double }
|
240
|
+
RequestCalculateOptionPrice = CalculateOptionPrice = RequestOptionPrice =
|
241
|
+
def_message(55,
|
242
|
+
[:contract, :serialize_long, [:con_id]],
|
243
|
+
:volatility,
|
244
|
+
:under_price)
|
245
|
+
|
136
246
|
# Start receiving market scanner results through the ScannerData messages.
|
137
247
|
# @data = { :id => ticker_id (int),
|
138
248
|
# :number_of_rows => int: number of rows of data to return for a query.
|
@@ -176,79 +286,97 @@ module IB
|
|
176
286
|
# To learn all valid parameter values that a scanner subscription can have,
|
177
287
|
# first subscribe to ScannerParameters and send RequestScannerParameters message.
|
178
288
|
# Available scanner parameters values will be listed in received XML document.
|
179
|
-
|
180
|
-
|
181
|
-
|
289
|
+
RequestScannerSubscription =
|
290
|
+
def_message([22, 3],
|
291
|
+
[:number_of_rows, -1], # was: EOL,
|
292
|
+
:instrument,
|
293
|
+
:location_code,
|
294
|
+
:scan_code,
|
295
|
+
[:above_price, EOL],
|
296
|
+
[:below_price, EOL],
|
297
|
+
[:above_volume, EOL],
|
298
|
+
[:market_cap_above, EOL],
|
299
|
+
[:market_cap_below, EOL],
|
300
|
+
:moody_rating_above,
|
301
|
+
:moody_rating_below,
|
302
|
+
:sp_rating_above,
|
303
|
+
:sp_rating_below,
|
304
|
+
:maturity_date_above,
|
305
|
+
:maturity_date_below,
|
306
|
+
[:coupon_rate_above, EOL],
|
307
|
+
[:coupon_rate_below, EOL],
|
308
|
+
:exclude_convertible,
|
309
|
+
[:average_option_volume_above, EOL], # ?
|
310
|
+
:scanner_setting_pairs,
|
311
|
+
:stock_type_filter)
|
312
|
+
|
313
|
+
### Even more complex Outgoing Message classes, overriding #encode method:
|
314
|
+
|
315
|
+
# Data format is { :id => order_id (int),
|
316
|
+
# :contract => Contract,
|
317
|
+
# :order => Order }
|
318
|
+
class PlaceOrder < AbstractMessage
|
319
|
+
@message_id = 3
|
320
|
+
@version = 31
|
182
321
|
|
183
322
|
def encode
|
184
323
|
[super,
|
185
|
-
@data[:
|
186
|
-
@data[:instrument],
|
187
|
-
@data[:location_code],
|
188
|
-
@data[:scan_code],
|
189
|
-
@data[:above_price] || EOL,
|
190
|
-
@data[:below_price] || EOL,
|
191
|
-
@data[:above_volume] || EOL,
|
192
|
-
@data[:market_cap_above] || EOL,
|
193
|
-
@data[:market_cap_below] || EOL,
|
194
|
-
@data[:moody_rating_above],
|
195
|
-
@data[:moody_rating_below],
|
196
|
-
@data[:sp_rating_above],
|
197
|
-
@data[:sp_rating_below],
|
198
|
-
@data[:maturity_date_above],
|
199
|
-
@data[:maturity_date_below],
|
200
|
-
@data[:coupon_rate_above] || EOL,
|
201
|
-
@data[:coupon_rate_below] || EOL,
|
202
|
-
@data[:exclude_convertible],
|
203
|
-
@data[:average_option_volume_above] || EOL, # ?
|
204
|
-
@data[:scanner_setting_pairs],
|
205
|
-
@data[:stock_type_filter]
|
206
|
-
]
|
324
|
+
@data[:order].serialize_with(@data[:contract])].flatten
|
207
325
|
end
|
208
|
-
end #
|
326
|
+
end # PlaceOrder
|
209
327
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
#
|
231
|
-
|
232
|
-
|
233
|
-
|
328
|
+
module DataParser
|
329
|
+
# Preprocessor for some data fields
|
330
|
+
def parse data
|
331
|
+
data_type = DATA_TYPES[data[:what_to_show]] || data[:what_to_show]
|
332
|
+
unless DATA_TYPES.values.include?(data_type)
|
333
|
+
raise ArgumentError.new(":what_to_show must be one of #{DATA_TYPES.inspect}.")
|
334
|
+
end
|
335
|
+
|
336
|
+
bar_size = BAR_SIZES[data[:bar_size]] || data[:bar_size]
|
337
|
+
unless BAR_SIZES.values.include?(bar_size)
|
338
|
+
raise ArgumentError.new(":bar_size must be one of #{BAR_SIZES.inspect}.")
|
339
|
+
end
|
340
|
+
|
341
|
+
contract = data[:contract].is_a?(Models::Contract) ?
|
342
|
+
data[:contract] : Models::Contract.from_ib_ruby(data[:contract])
|
343
|
+
|
344
|
+
[data_type, bar_size, contract]
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
# data = { :id => ticker_id (int),
|
349
|
+
# :contract => Contract ,
|
350
|
+
# :bar_size => int/Symbol? Currently only 5 second bars (2?) are supported,
|
351
|
+
# if any other value is used, an exception will be thrown.,
|
352
|
+
# :what_to_show => Symbol: Determines the nature of data being extracted.
|
353
|
+
# Valid values:
|
354
|
+
# :trades, :midpoint, :bid, :ask, :bid_ask,
|
355
|
+
# :historical_volatility, :option_implied_volatility,
|
356
|
+
# :option_volume, :option_open_interest
|
357
|
+
# - converts to "TRADES," "MIDPOINT," "BID," etc...
|
358
|
+
# :use_rth => int: 0 - all data available during the time span requested
|
359
|
+
# is returned, even data bars covering time intervals where the
|
360
|
+
# market in question was illiquid. 1 - only data within the
|
361
|
+
# "Regular Trading Hours" of the product in question is returned,
|
362
|
+
# even if the time span requested falls partially or completely
|
363
|
+
# outside of them.
|
364
|
+
class RequestRealTimeBars < AbstractMessage
|
365
|
+
@message_id = 50
|
366
|
+
@version = 1 # ?
|
367
|
+
|
368
|
+
include DataParser
|
234
369
|
|
235
370
|
def encode
|
236
|
-
|
237
|
-
|
238
|
-
''
|
239
|
-
when Array
|
240
|
-
@data[:tick_list].join ','
|
241
|
-
when String
|
242
|
-
@data[:tick_list]
|
243
|
-
end
|
371
|
+
data_type, bar_size, contract = parse @data
|
372
|
+
|
244
373
|
[super,
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
@data[:snapshot] || false]
|
374
|
+
contract.serialize_long,
|
375
|
+
bar_size,
|
376
|
+
data_type.to_s.upcase,
|
377
|
+
@data[:use_rth]].flatten
|
250
378
|
end
|
251
|
-
end #
|
379
|
+
end # RequestRealTimeBars
|
252
380
|
|
253
381
|
# data = { :id => int: Ticker id, needs to be different than the reqMktData ticker
|
254
382
|
# id. If you use the same ticker ID you used for the symbol when
|
@@ -325,19 +453,10 @@ module IB
|
|
325
453
|
@message_id = 20
|
326
454
|
@version = 4
|
327
455
|
|
328
|
-
|
329
|
-
data_type = DATA_TYPES[@data[:what_to_show]] || @data[:what_to_show]
|
330
|
-
unless DATA_TYPES.values.include?(data_type)
|
331
|
-
raise ArgumentError(":what_to_show must be one of #{DATA_TYPES}.")
|
332
|
-
end
|
333
|
-
|
334
|
-
bar_size = BAR_SIZES[@data[:bar_size]] || @data[:bar_size]
|
335
|
-
unless BAR_SIZES.values.include?(bar_size)
|
336
|
-
raise ArgumentError(":bar_size must be one of #{BAR_SIZES}.")
|
337
|
-
end
|
456
|
+
include DataParser
|
338
457
|
|
339
|
-
|
340
|
-
|
458
|
+
def encode
|
459
|
+
data_type, bar_size, contract = parse @data
|
341
460
|
|
342
461
|
[super,
|
343
462
|
contract.serialize_long(:include_expired),
|
@@ -347,193 +466,10 @@ module IB
|
|
347
466
|
@data[:use_rth],
|
348
467
|
data_type.to_s.upcase,
|
349
468
|
@data[:format_date],
|
350
|
-
contract.serialize_legs]
|
469
|
+
contract.serialize_legs].flatten
|
351
470
|
end
|
352
471
|
end # RequestHistoricalData
|
353
472
|
|
354
|
-
# data = { :id => ticker_id (int),
|
355
|
-
# :contract => Contract ,
|
356
|
-
# :bar_size => int/Symbol? Currently only 5 second bars (2?) are supported,
|
357
|
-
# if any other value is used, an exception will be thrown.,
|
358
|
-
# :what_to_show => Symbol: Determines the nature of data being extracted.
|
359
|
-
# Valid values:
|
360
|
-
# :trades, :midpoint, :bid, :ask, :bid_ask,
|
361
|
-
# :historical_volatility, :option_implied_volatility,
|
362
|
-
# :option_volume, :option_open_interest
|
363
|
-
# - converts to "TRADES," "MIDPOINT," "BID," etc...
|
364
|
-
# :use_rth => int: 0 - all data available during the time span requested
|
365
|
-
# is returned, even data bars covering time intervals where the
|
366
|
-
# market in question was illiquid. 1 - only data within the
|
367
|
-
# "Regular Trading Hours" of the product in question is returned,
|
368
|
-
# even if the time span requested falls partially or completely
|
369
|
-
# outside of them.
|
370
|
-
class RequestRealTimeBars < AbstractMessage
|
371
|
-
@message_id = 50
|
372
|
-
|
373
|
-
def encode
|
374
|
-
data_type = DATA_TYPES[@data[:what_to_show]] || @data[:what_to_show]
|
375
|
-
unless DATA_TYPES.values.include?(data_type)
|
376
|
-
raise ArgumentError(":what_to_show must be one of #{DATA_TYPES}.")
|
377
|
-
end
|
378
|
-
|
379
|
-
bar_size = BAR_SIZES[@data[:bar_size]] || @data[:bar_size]
|
380
|
-
unless BAR_SIZES.values.include?(bar_size)
|
381
|
-
raise ArgumentError(":bar_size must be one of #{BAR_SIZES}.")
|
382
|
-
end
|
383
|
-
|
384
|
-
contract = @data[:contract].is_a?(Models::Contract) ?
|
385
|
-
@data[:contract] : Models::Contract.from_ib_ruby(@data[:contract])
|
386
|
-
|
387
|
-
[super,
|
388
|
-
contract.serialize_long,
|
389
|
-
bar_size,
|
390
|
-
data_type.to_s.upcase,
|
391
|
-
@data[:use_rth]]
|
392
|
-
end
|
393
|
-
end # RequestRealTimeBars
|
394
|
-
|
395
|
-
# data => { :id => request_id (int), :contract => Contract }
|
396
|
-
class RequestContractData < AbstractMessage
|
397
|
-
@message_id = 9
|
398
|
-
@version = 6
|
399
|
-
|
400
|
-
def encode
|
401
|
-
[super,
|
402
|
-
@data[:contract].serialize_short(:con_id, :include_expired, :sec_id)]
|
403
|
-
end
|
404
|
-
end # RequestContractData
|
405
|
-
RequestContractDetails = RequestContractData # alias
|
406
|
-
|
407
|
-
# data = { :id => ticker_id (int), :contract => Contract, :num_rows => int }
|
408
|
-
class RequestMarketDepth < AbstractMessage
|
409
|
-
@message_id = 10
|
410
|
-
@version = 3
|
411
|
-
|
412
|
-
def encode
|
413
|
-
[super,
|
414
|
-
@data[:contract].serialize_short,
|
415
|
-
@data[:num_rows]]
|
416
|
-
end
|
417
|
-
end # RequestMarketDepth
|
418
|
-
|
419
|
-
# data = { :id => ticker_id (int),
|
420
|
-
# :contract => Contract,
|
421
|
-
# :exercise_action => int, 1 = exercise, 2 = lapse
|
422
|
-
# :exercise_quantity => int, The number of contracts to be exercised
|
423
|
-
# :account => string,
|
424
|
-
# :override => int: Specifies whether your setting will override the
|
425
|
-
# system's natural action. For example, if your action
|
426
|
-
# is "exercise" and the option is not in-the-money, by
|
427
|
-
# natural action the option would not exercise. If you
|
428
|
-
# have override set to "yes" the natural action would be
|
429
|
-
# overridden and the out-of-the money option would be
|
430
|
-
# exercised. Values are:
|
431
|
-
# � 0 = do not override
|
432
|
-
# � 1 = override
|
433
|
-
# }
|
434
|
-
class ExerciseOptions < AbstractMessage
|
435
|
-
@message_id = 21
|
436
|
-
|
437
|
-
def encode
|
438
|
-
[super,
|
439
|
-
@data[:contract].serialize_short,
|
440
|
-
@data[:exercise_action],
|
441
|
-
@data[:exercise_quantity],
|
442
|
-
@data[:account],
|
443
|
-
@data[:override]]
|
444
|
-
end
|
445
|
-
end # ExerciseOptions
|
446
|
-
|
447
|
-
# Data format is { :id => order_id (int),
|
448
|
-
# :contract => Contract,
|
449
|
-
# :order => Order }
|
450
|
-
class PlaceOrder < AbstractMessage
|
451
|
-
@message_id = 3
|
452
|
-
@version = 31
|
453
|
-
|
454
|
-
def encode
|
455
|
-
[super,
|
456
|
-
@data[:order].serialize_with(@data[:contract])]
|
457
|
-
end
|
458
|
-
end # PlaceOrder
|
459
|
-
|
460
|
-
# When this message is sent, TWS responds with ExecutionData messages, each
|
461
|
-
# containing the execution report that meets the specified criteria.
|
462
|
-
# @data={:id => int: :request_id,
|
463
|
-
# :client_id => int: Filter the results based on the clientId.
|
464
|
-
# :acct_code => Filter the results based on based on account code.
|
465
|
-
# Note: this is only relevant for Financial Advisor accts.
|
466
|
-
# :sec_type => Filter the results based on the order security type.
|
467
|
-
# :time => Filter the results based on execution reports received
|
468
|
-
# after the specified time - format "yyyymmdd-hh:mm:ss"
|
469
|
-
# :symbol => Filter the results based on the order symbol.
|
470
|
-
# :exchange => Filter the results based on the order exchange
|
471
|
-
# :side => Filter the results based on the order action: BUY/SELL/SSHORT
|
472
|
-
class RequestExecutions < AbstractMessage
|
473
|
-
@message_id = 7
|
474
|
-
@version = 3
|
475
|
-
|
476
|
-
def encode
|
477
|
-
[super,
|
478
|
-
@data[:client_id],
|
479
|
-
@data[:acct_code],
|
480
|
-
@data[:time], # Valid format for time is "yyyymmdd-hh:mm:ss"
|
481
|
-
@data[:symbol],
|
482
|
-
@data[:sec_type],
|
483
|
-
@data[:exchange],
|
484
|
-
@data[:side]]
|
485
|
-
end
|
486
|
-
end # RequestExecutions
|
487
|
-
|
488
|
-
# Send this message to receive Reuters global fundamental data. There must be
|
489
|
-
# a subscription to Reuters Fundamental set up in Account Management before
|
490
|
-
# you can receive this data.
|
491
|
-
# data = { :id => int: :request_id,
|
492
|
-
# :contract => Contract,
|
493
|
-
# :report_type => String: one of the following:
|
494
|
-
# 'Estimates', 'Financial Statements', 'Summary' }
|
495
|
-
class RequestFundamentalData < AbstractMessage
|
496
|
-
@message_id = 52
|
497
|
-
|
498
|
-
def encode
|
499
|
-
[super,
|
500
|
-
@data[:request_id],
|
501
|
-
@data[:contract].serialize(:primary_exchange), # Minimal serialization set
|
502
|
-
@data[:report_type]]
|
503
|
-
end
|
504
|
-
end # RequestFundamentalData
|
505
|
-
|
506
|
-
# data = { :request_id => int, :contract => Contract,
|
507
|
-
# :option_price => double, :under_price => double }
|
508
|
-
class RequestImpliedVolatility < AbstractMessage
|
509
|
-
@message_id = 54
|
510
|
-
|
511
|
-
def encode
|
512
|
-
[super,
|
513
|
-
@data[:request_id],
|
514
|
-
@data[:contract].serialize_long(:con_id),
|
515
|
-
@data[:option_price],
|
516
|
-
@data[:under_price]]
|
517
|
-
end
|
518
|
-
end # RequestImpliedVolatility
|
519
|
-
CalculateImpliedVolatility = RequestImpliedVolatility
|
520
|
-
RequestCalculateImpliedVolatility = RequestImpliedVolatility
|
521
|
-
|
522
|
-
# data = { :request_id => int, :contract => Contract,
|
523
|
-
# :volatility => double, :under_price => double }
|
524
|
-
class RequestOptionPrice < AbstractMessage
|
525
|
-
@message_id = 55
|
526
|
-
|
527
|
-
def encode
|
528
|
-
[super,
|
529
|
-
@data[:request_id],
|
530
|
-
@data[:contract].serialize_long(:con_id),
|
531
|
-
@data[:volatility],
|
532
|
-
@data[:under_price]]
|
533
|
-
end
|
534
|
-
end # RequestOptionPrice
|
535
|
-
CalculateOptionPrice = RequestOptionPrice
|
536
|
-
RequestCalculateOptionPrice = RequestOptionPrice
|
537
473
|
|
538
474
|
end # module Outgoing
|
539
475
|
end # module Messages
|
data/lib/ib-ruby/models/bar.rb
CHANGED
@@ -5,7 +5,7 @@ module IB
|
|
5
5
|
# This is a single data point delivered by HistoricData messages.
|
6
6
|
# Instantiate with a Hash of attributes, to be auto-set via initialize in Model.
|
7
7
|
class Bar < Model
|
8
|
-
attr_accessor :
|
8
|
+
attr_accessor :time, # The date-time stamp of the start of the bar. The format is
|
9
9
|
# determined by the reqHistoricalData() formatDate parameter.
|
10
10
|
:open, # The bar opening price.
|
11
11
|
:high, # The high price during the time covered by the bar.
|
@@ -18,8 +18,8 @@ module IB
|
|
18
18
|
# of trades that occurred during the time period the bar covers
|
19
19
|
|
20
20
|
def to_s
|
21
|
-
"<Bar #{
|
22
|
-
(
|
21
|
+
"<Bar #{time}: wap: #{wap}, OHLC: #{open}, #{high}, #{low}, #{close}, " +
|
22
|
+
(trades ? "trades: #{trades}," : "") + " vol: #{volume}, gaps? #{has_gaps}>"
|
23
23
|
end
|
24
24
|
end # class Bar
|
25
25
|
end # module Models
|
@@ -23,11 +23,7 @@ module IB
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def to_human
|
26
|
-
"<Bag:
|
27
|
-
(@legs_description ||
|
28
|
-
@legs.map do |leg|
|
29
|
-
"#{leg.action} #{leg.ratio} * #{leg.con_id}"
|
30
|
-
end.join('|')) + ">"
|
26
|
+
"<Bag: #{[symbol, exchange, currency].join(' ')} legs: #{legs_description} >"
|
31
27
|
end
|
32
28
|
|
33
29
|
end # class Bag
|