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