ib-ruby 0.5.18 → 0.5.19
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 +21 -20
- data/VERSION +1 -1
- data/bin/cancel_orders +1 -1
- data/bin/list_orders +1 -2
- data/lib/ib-ruby/connection.rb +25 -8
- data/lib/ib-ruby/constants.rb +1 -0
- data/lib/ib-ruby/messages/incoming.rb +5 -5
- data/lib/ib-ruby/messages/outgoing.rb +21 -21
- data/lib/ib-ruby/models/contract.rb +52 -2
- data/lib/ib-ruby/models/model.rb +10 -0
- data/lib/ib-ruby/models/order.rb +34 -2
- data/lib/ib-ruby/socket.rb +1 -1
- data/spec/ib-ruby/connection_spec.rb +24 -17
- data/spec/ib-ruby/messages/account_info_spec.rb +5 -5
- data/spec/ib-ruby/messages/just_connect_spec.rb +3 -1
- data/spec/ib-ruby/messages/market_data_spec.rb +3 -3
- data/spec/ib-ruby/messages/open_order +273 -0
- data/spec/ib-ruby/messages/orders_spec.rb +219 -0
- data/spec/ib-ruby/models/contract_spec.rb +65 -69
- data/spec/ib-ruby/models/order_spec.rb +34 -12
- data/spec/message_helper.rb +16 -2
- data/spec/spec_helper.rb +16 -1
- metadata +7 -3
data/HISTORY
CHANGED
data/TODO
CHANGED
@@ -1,29 +1,30 @@
|
|
1
|
-
1.
|
2
|
-
http://finance.groups.yahoo.com/group/TWSAPI/message/25413
|
1
|
+
1. Create integration tests for basic use cases
|
3
2
|
|
4
|
-
|
5
|
-
(potentially adding other broker adapters)
|
3
|
+
Plan:
|
6
4
|
|
7
|
-
|
5
|
+
1. Create integration tests (Brokerton?)
|
8
6
|
|
9
|
-
|
7
|
+
2. Make ActiveModel-like attributes Hash for easy attributes updating
|
10
8
|
|
11
|
-
|
9
|
+
3. IB#send_message method should accept block, thus compressing subscribe/send_message
|
12
10
|
pair into a single call - to simplify DSL.
|
13
11
|
|
12
|
+
4. IB Connection uses delays to prevent hitting 50 msgs/sec limit:
|
13
|
+
http://finance.groups.yahoo.com/group/TWSAPI/message/25413
|
14
|
+
|
15
|
+
5. IB Connection reconnects gracefully in case if TWS disconnects/reconnects
|
16
|
+
|
14
17
|
6. Compatibility check for new TWS v.966
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
run at /Users/vb/Dev/ib/option_mower/lib/option_mower/robot.rb:121
|
28
|
-
(root) at bin/option_mower:46
|
19
|
+
|
20
|
+
Done:
|
21
|
+
|
22
|
+
2. IB#subscribe should accept regexes.
|
23
|
+
|
24
|
+
Ideas for future:
|
25
|
+
|
26
|
+
1. Decouple Broker-specific Adapter from universal high-level messaging layer
|
27
|
+
(potentially adding other broker adapters)
|
28
|
+
|
29
|
+
2. Tweak IB::Message API for speed (use class methods)
|
29
30
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.19
|
data/bin/cancel_orders
CHANGED
@@ -21,7 +21,7 @@ ib.subscribe(:Alert, :OpenOrder, :OrderStatus, :OpenOrderEnd) { |msg| puts msg.t
|
|
21
21
|
if ARGV.empty?
|
22
22
|
ib.send_message :RequestGlobalCancel
|
23
23
|
else
|
24
|
-
|
24
|
+
ib.cancel_order *ARGV
|
25
25
|
end
|
26
26
|
|
27
27
|
ib.send_message :RequestAllOpenOrders
|
data/bin/list_orders
CHANGED
data/lib/ib-ruby/connection.rb
CHANGED
@@ -92,7 +92,7 @@ module IB
|
|
92
92
|
alias open connect # Legacy alias
|
93
93
|
|
94
94
|
def disconnect
|
95
|
-
if
|
95
|
+
if reader_running?
|
96
96
|
@reader_running = false
|
97
97
|
@server[:reader].join
|
98
98
|
end
|
@@ -109,6 +109,10 @@ module IB
|
|
109
109
|
@connected
|
110
110
|
end
|
111
111
|
|
112
|
+
def reader_running?
|
113
|
+
@reader_running && @server[:reader] && @server[:reader].alive?
|
114
|
+
end
|
115
|
+
|
112
116
|
# Subscribe Proc or block to specific type(s) of incoming message events.
|
113
117
|
# Listener will be called later with received message instance as its argument.
|
114
118
|
# Returns subscriber id to allow unsubscribing
|
@@ -119,18 +123,21 @@ module IB
|
|
119
123
|
raise ArgumentError.new "Need subscriber proc or block" unless subscriber.is_a? Proc
|
120
124
|
|
121
125
|
args.each do |what|
|
122
|
-
|
126
|
+
message_classes =
|
123
127
|
case
|
124
128
|
when what.is_a?(Class) && what < Messages::Incoming::AbstractMessage
|
125
129
|
what
|
126
130
|
when what.is_a?(Symbol)
|
127
131
|
Messages::Incoming.const_get(what)
|
132
|
+
when what.is_a?(Regexp)
|
133
|
+
Messages::Incoming::Table.values.find_all { |klass| klass.to_s =~ what }
|
128
134
|
else
|
129
135
|
raise ArgumentError.new "#{what} must represent incoming IB message class"
|
130
136
|
end
|
131
|
-
|
132
|
-
|
133
|
-
|
137
|
+
[message_classes].flatten.each do |message_class|
|
138
|
+
# TODO: Fix: RuntimeError: can't add a new key into hash during iteration
|
139
|
+
subscribers[message_class][subscriber_id] = subscriber
|
140
|
+
end
|
134
141
|
end
|
135
142
|
subscriber_id
|
136
143
|
end
|
@@ -190,14 +197,24 @@ module IB
|
|
190
197
|
end
|
191
198
|
|
192
199
|
# Place Order (convenience wrapper for message :PlaceOrder).
|
193
|
-
#
|
200
|
+
# Assigns client_id and order_id fields to placed order.
|
201
|
+
# Returns order_id.
|
194
202
|
def place_order order, contract
|
195
|
-
@next_order_id += 1
|
196
203
|
send_message :PlaceOrder,
|
197
204
|
:order => order,
|
198
205
|
:contract => contract,
|
199
206
|
:id => @next_order_id
|
200
|
-
@
|
207
|
+
order.client_id = @server[:client_id]
|
208
|
+
order.order_id = @next_order_id
|
209
|
+
@next_order_id += 1
|
210
|
+
order.order_id
|
211
|
+
end
|
212
|
+
|
213
|
+
# Cancel Orders by their id (convenience wrapper for message :CancelOrder).
|
214
|
+
def cancel_order *order_ids
|
215
|
+
order_ids.each do |order_id|
|
216
|
+
send_message :CancelOrder, :id => order_id.to_i
|
217
|
+
end
|
201
218
|
end
|
202
219
|
|
203
220
|
# Start reader thread that continuously reads messages from server in background.
|
data/lib/ib-ruby/constants.rb
CHANGED
@@ -534,9 +534,9 @@ module IB
|
|
534
534
|
@order.init_margin = @socket.read_string
|
535
535
|
@order.maint_margin = @socket.read_string
|
536
536
|
@order.equity_with_loan = @socket.read_string
|
537
|
-
@order.commission = @socket.read_decimal_max
|
538
|
-
@order.min_commission = @socket.read_decimal_max
|
539
|
-
@order.max_commission = @socket.read_decimal_max
|
537
|
+
@order.commission = @socket.read_decimal_max # May be nil!
|
538
|
+
@order.min_commission = @socket.read_decimal_max # May be nil!
|
539
|
+
@order.max_commission = @socket.read_decimal_max # May be nil!
|
540
540
|
@order.commission_currency = @socket.read_string
|
541
541
|
@order.warning_text = @socket.read_string
|
542
542
|
end
|
@@ -568,8 +568,8 @@ module IB
|
|
568
568
|
[:market_price, :decimal],
|
569
569
|
[:market_value, :decimal],
|
570
570
|
[:average_cost, :decimal],
|
571
|
-
[:unrealized_pnl, :
|
572
|
-
[:realized_pnl, :
|
571
|
+
[:unrealized_pnl, :decimal_max], # May be nil!
|
572
|
+
[:realized_pnl, :decimal_max], # May be nil!
|
573
573
|
[:account_name, :string]
|
574
574
|
end
|
575
575
|
|
@@ -277,19 +277,12 @@ module IB
|
|
277
277
|
# '30 min'
|
278
278
|
# '1 hour'
|
279
279
|
# '1 day'
|
280
|
-
# :what_to_show => Symbol:
|
281
|
-
#
|
282
|
-
#
|
283
|
-
#
|
284
|
-
|
285
|
-
|
286
|
-
#� ASK
|
287
|
-
#� BID_ASK
|
288
|
-
#� HISTORICAL_VOLATILITY
|
289
|
-
#� OPTION_IMPLIED_VOLATILITY
|
290
|
-
#� OPTION_VOLUME
|
291
|
-
#� OPTION_OPEN_INTEREST
|
292
|
-
# converts to "TRADES," "MIDPOINT," "BID," or "ASK."
|
280
|
+
# :what_to_show => Symbol: Determines the nature of data being extracted.
|
281
|
+
# Valid values:
|
282
|
+
# :trades, :midpoint, :bid, :ask, :bid_ask,
|
283
|
+
# :historical_volatility, :option_implied_volatility,
|
284
|
+
# :option_volume, :option_open_interest
|
285
|
+
# - converts to "TRADES," "MIDPOINT," "BID," etc...
|
293
286
|
# :use_rth => int: 0 - all data available during the time span requested
|
294
287
|
# is returned, even data bars covering time intervals where the
|
295
288
|
# market in question was illiquid. 1 - only data within the
|
@@ -362,8 +355,12 @@ module IB
|
|
362
355
|
# :contract => Contract ,
|
363
356
|
# :bar_size => int/Symbol? Currently only 5 second bars (2?) are supported,
|
364
357
|
# if any other value is used, an exception will be thrown.,
|
365
|
-
# :what_to_show => Symbol:
|
366
|
-
#
|
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...
|
367
364
|
# :use_rth => int: 0 - all data available during the time span requested
|
368
365
|
# is returned, even data bars covering time intervals where the
|
369
366
|
# market in question was illiquid. 1 - only data within the
|
@@ -374,20 +371,23 @@ module IB
|
|
374
371
|
@message_id = 50
|
375
372
|
|
376
373
|
def encode
|
377
|
-
|
378
|
-
|
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}.")
|
379
377
|
end
|
380
378
|
|
381
|
-
|
382
|
-
|
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
383
|
|
384
384
|
contract = @data[:contract].is_a?(Models::Contract) ?
|
385
385
|
@data[:contract] : Models::Contract.from_ib_ruby(@data[:contract])
|
386
386
|
|
387
387
|
[super,
|
388
388
|
contract.serialize_long,
|
389
|
-
|
390
|
-
|
389
|
+
bar_size,
|
390
|
+
data_type.to_s.upcase,
|
391
391
|
@data[:use_rth]]
|
392
392
|
end
|
393
393
|
end # RequestRealTimeBars
|
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'ib-ruby/models/model'
|
2
2
|
|
3
|
-
# TODO: Implement equals() according to the criteria in IB's Java client.
|
4
|
-
|
5
3
|
module IB
|
6
4
|
module Models
|
7
5
|
class Contract < Model
|
@@ -242,6 +240,8 @@ module IB
|
|
242
240
|
end
|
243
241
|
end
|
244
242
|
|
243
|
+
### Leg-related methods (better suited to BAG subclass?)
|
244
|
+
|
245
245
|
# Some messages send open_close too, some don't. WTF.
|
246
246
|
# "BAG" is not really a contract, but a combination (combo) of securities.
|
247
247
|
# AKA basket or bag of securities. Individual securities in combo are represented
|
@@ -252,6 +252,56 @@ module IB
|
|
252
252
|
[legs.size, legs.map { |leg| leg.serialize *fields }]
|
253
253
|
end
|
254
254
|
|
255
|
+
# Check if two Contracts have same legs (maybe in different order)
|
256
|
+
def same_legs? other
|
257
|
+
legs == other.legs ||
|
258
|
+
legs_description.split(',').sort == other.legs_description.split(',').sort
|
259
|
+
end
|
260
|
+
|
261
|
+
# IB-equivalent leg description. TODO: Rewrite with self[:legs_description]
|
262
|
+
def legs_description
|
263
|
+
@legs_description || legs.map { |leg| "#{leg.con_id}|#{leg.weight}" }.join(',')
|
264
|
+
end
|
265
|
+
|
266
|
+
# Contract comparison
|
267
|
+
def == other
|
268
|
+
# Different sec_id_type
|
269
|
+
return false if sec_id_type && other.sec_id_type && sec_id_type != other.sec_id_type
|
270
|
+
|
271
|
+
# Different sec_id
|
272
|
+
return false if sec_id && other.sec_id && sec_id != other.sec_id
|
273
|
+
|
274
|
+
# Different under_comp
|
275
|
+
return false if under_comp && other.under_comp && under_comp != other.under_comp
|
276
|
+
|
277
|
+
# Different legs
|
278
|
+
return false unless same_legs? other
|
279
|
+
|
280
|
+
# Same con_id for all Bags, but unknown for new Contracts...
|
281
|
+
# 0 or nil con_id matches any
|
282
|
+
return false if con_id != 0 && other.con_id != 0 &&
|
283
|
+
con_id && other.con_id && con_id != other.con_id
|
284
|
+
|
285
|
+
# SMART or nil exchange matches any
|
286
|
+
return false if exchange != 'SMART' && other.exchange != 'SMART' &&
|
287
|
+
exchange && other.exchange && exchange != other.exchange
|
288
|
+
|
289
|
+
# Comparison for Bonds and Options
|
290
|
+
if sec_type == SECURITY_TYPES[:bond] || sec_type == SECURITY_TYPES[:option]
|
291
|
+
return false unless right == other.right &&
|
292
|
+
strike == other.strike &&
|
293
|
+
expiry == other.expiry &&
|
294
|
+
multiplier == other.multiplier
|
295
|
+
end
|
296
|
+
|
297
|
+
# All else being equal...
|
298
|
+
sec_type == other.sec_type &&
|
299
|
+
symbol == other.symbol &&
|
300
|
+
currency == other.currency
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
# TODO: Remove @summary into reader method
|
255
305
|
def to_s
|
256
306
|
"<Contract: " + instance_variables.map do |key|
|
257
307
|
unless key == :@summary
|
data/lib/ib-ruby/models/model.rb
CHANGED
@@ -14,6 +14,16 @@ module IB
|
|
14
14
|
|
15
15
|
opts.keys.each { |key| self.send("#{key}=", opts[key]) }
|
16
16
|
end
|
17
|
+
|
18
|
+
# ActiveModel-style attribute accessors
|
19
|
+
def [] key
|
20
|
+
self.send key
|
21
|
+
end
|
22
|
+
|
23
|
+
def []= key, val
|
24
|
+
self.send "#{key}=", val
|
25
|
+
end
|
26
|
+
|
17
27
|
end # Model
|
18
28
|
end # module Models
|
19
29
|
end # module IB
|
data/lib/ib-ruby/models/order.rb
CHANGED
@@ -46,7 +46,7 @@ module IB
|
|
46
46
|
Max_Value = 99999999
|
47
47
|
|
48
48
|
# Main order fields
|
49
|
-
attr_accessor :id, # int: m_orderId?
|
49
|
+
attr_accessor :id, # int: m_orderId? Order id associated with client (volatile).
|
50
50
|
:client_id, # int: The id of the client that placed this order.
|
51
51
|
:perm_id, # int: TWS id used to identify orders, remains
|
52
52
|
# the same over TWS sessions.
|
@@ -304,6 +304,9 @@ module IB
|
|
304
304
|
|
305
305
|
:warning_text # String: Displays a warning message if warranted.
|
306
306
|
|
307
|
+
alias order_id id # TODO: Change due to ActiveRecord specifics
|
308
|
+
alias order_id= id= # TODO: Change due to ActiveRecord specifics
|
309
|
+
|
307
310
|
# IB uses weird String with Java Double.MAX_VALUE to indicate no value here
|
308
311
|
def init_margin= val
|
309
312
|
@init_margin = val == "1.7976931348623157E308" ? nil : val
|
@@ -321,6 +324,10 @@ module IB
|
|
321
324
|
|
322
325
|
def initialize opts = {}
|
323
326
|
# Assign defaults first!
|
327
|
+
@aux_price = 0.0
|
328
|
+
@parent_id=0
|
329
|
+
@tif='DAY'
|
330
|
+
|
324
331
|
@outside_rth = false
|
325
332
|
@open_close = "O"
|
326
333
|
@origin = Origin_Customer
|
@@ -429,6 +436,31 @@ module IB
|
|
429
436
|
what_if]
|
430
437
|
end
|
431
438
|
|
439
|
+
# Order comparison
|
440
|
+
def == other
|
441
|
+
perm_id == other.perm_id ||
|
442
|
+
order_id == other.order_id && # ((p __LINE__)||true) &&
|
443
|
+
client_id == other.client_id &&
|
444
|
+
parent_id == other.parent_id &&
|
445
|
+
tif == other.tif &&
|
446
|
+
action == other.action &&
|
447
|
+
order_type == other.order_type &&
|
448
|
+
total_quantity == other.total_quantity &&
|
449
|
+
limit_price == other.limit_price &&
|
450
|
+
aux_price == other.aux_price &&
|
451
|
+
outside_rth == other.outside_rth &&
|
452
|
+
origin == other.origin &&
|
453
|
+
transmit == other.transmit &&
|
454
|
+
designated_location == other.designated_location &&
|
455
|
+
exempt_code == other.exempt_code &&
|
456
|
+
what_if == other.what_if &&
|
457
|
+
not_held == other.not_held &&
|
458
|
+
algo_strategy == other.algo_strategy &&
|
459
|
+
algo_params == other.algo_params
|
460
|
+
|
461
|
+
# TODO: || compare all attributes!
|
462
|
+
end
|
463
|
+
|
432
464
|
def serialize_algo
|
433
465
|
if algo_strategy.nil? || algo_strategy.empty?
|
434
466
|
['']
|
@@ -448,7 +480,7 @@ module IB
|
|
448
480
|
|
449
481
|
def to_human
|
450
482
|
"<Order: #{order_type} #{tif} #{action} #{total_quantity} #{status} #{limit_price}" +
|
451
|
-
" id: #{
|
483
|
+
" id: #{order_id}/#{perm_id} from: #{client_id}/#{account}>"
|
452
484
|
end
|
453
485
|
end # class Order
|
454
486
|
end # module Models
|
data/lib/ib-ruby/socket.rb
CHANGED
@@ -45,7 +45,7 @@ module IB
|
|
45
45
|
# Floating-point numbers shouldn't be used to store money...
|
46
46
|
# ...but BigDecimals are too unwieldy to use in this case... maybe later
|
47
47
|
# str.nil? || str.empty? ? nil : str.to_d
|
48
|
-
str.nil? || str.empty?
|
48
|
+
str.to_f unless str.nil? || str.empty? || str.to_f > 1.797 * 10.0 ** 306
|
49
49
|
end
|
50
50
|
end # class IBSocket
|
51
51
|
|
@@ -16,10 +16,10 @@ describe IB::Connection do
|
|
16
16
|
|
17
17
|
it { should_not be_nil }
|
18
18
|
it { should be_connected }
|
19
|
-
its(:server) {should be_a Hash}
|
20
|
-
its(:server) {should have_key :reader}
|
21
|
-
its(:subscribers) {should have_at_least(1).item} # :NextValidID and empty Hashes
|
22
|
-
its(:next_order_id) {should be_a Fixnum} # Not before :NextValidID arrives
|
19
|
+
its(:server) { should be_a Hash }
|
20
|
+
its(:server) { should have_key :reader }
|
21
|
+
its(:subscribers) { should have_at_least(1).item } # :NextValidID and empty Hashes
|
22
|
+
its(:next_order_id) { should be_a Fixnum } # Not before :NextValidID arrives
|
23
23
|
end
|
24
24
|
|
25
25
|
describe '#send_message', 'sending messages' do
|
@@ -46,24 +46,31 @@ describe IB::Connection do
|
|
46
46
|
context "subscriptions" do
|
47
47
|
|
48
48
|
it '#subscribe, adds(multiple) subscribers' do
|
49
|
-
@subscriber_id = @ib.subscribe(IB::Messages::Incoming::Alert, :
|
49
|
+
@subscriber_id = @ib.subscribe(IB::Messages::Incoming::Alert, :OpenOrder, /Value/) do
|
50
50
|
end
|
51
51
|
|
52
52
|
@subscriber_id.should be_a Fixnum
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
[IB::Messages::Incoming::Alert,
|
55
|
+
IB::Messages::Incoming::OpenOrder,
|
56
|
+
IB::Messages::Incoming::AccountValue,
|
57
|
+
IB::Messages::Incoming::PortfolioValue
|
58
|
+
].each do |message_class|
|
59
|
+
@ib.subscribers.should have_key(message_class)
|
60
|
+
@ib.subscribers[message_class].should have_key(@subscriber_id)
|
61
|
+
end
|
60
62
|
end
|
61
63
|
|
62
64
|
it '#unsubscribe, removes all subscribers at this id' do
|
63
65
|
@ib.unsubscribe(@subscriber_id)
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
+
[IB::Messages::Incoming::Alert,
|
68
|
+
IB::Messages::Incoming::OpenOrder,
|
69
|
+
IB::Messages::Incoming::AccountValue,
|
70
|
+
IB::Messages::Incoming::PortfolioValue
|
71
|
+
].each do |message_class|
|
72
|
+
@ib.subscribers[message_class].should_not have_key(@subscriber_id)
|
73
|
+
end
|
67
74
|
end
|
68
75
|
|
69
76
|
end # subscriptions
|
@@ -77,10 +84,10 @@ describe IB::Connection do
|
|
77
84
|
|
78
85
|
it { should_not be_nil }
|
79
86
|
it { should_not be_connected }
|
80
|
-
its(:server) {should be_a Hash}
|
81
|
-
its(:server) {should_not have_key :reader}
|
82
|
-
its(:subscribers) {should be_empty}
|
83
|
-
its(:next_order_id) {should be_nil}
|
87
|
+
its(:server) { should be_a Hash }
|
88
|
+
its(:server) { should_not have_key :reader }
|
89
|
+
its(:subscribers) { should be_empty }
|
90
|
+
its(:next_order_id) { should be_nil }
|
84
91
|
end
|
85
92
|
|
86
93
|
end # not connected
|
@@ -24,7 +24,7 @@ describe IB::Messages do
|
|
24
24
|
|
25
25
|
@ib.send_message :RequestAccountData, :subscribe => true
|
26
26
|
|
27
|
-
wait_for(5) {
|
27
|
+
wait_for(5) { received? :AccountDownloadEnd }
|
28
28
|
end
|
29
29
|
|
30
30
|
after(:all) do
|
@@ -35,7 +35,7 @@ describe IB::Messages do
|
|
35
35
|
context "received :Alert message " do
|
36
36
|
subject { @received[:Alert].first }
|
37
37
|
|
38
|
-
it {
|
38
|
+
it { should be_an IB::Messages::Incoming::Alert }
|
39
39
|
it { should be_warning }
|
40
40
|
it { should_not be_error }
|
41
41
|
its(:code) { should be_a Integer }
|
@@ -47,7 +47,7 @@ describe IB::Messages do
|
|
47
47
|
subject { @received[:AccountValue].first }
|
48
48
|
|
49
49
|
#ps
|
50
|
-
it {
|
50
|
+
it { should be_an IB::Messages::Incoming::AccountValue }
|
51
51
|
its(:data) { should be_a Hash }
|
52
52
|
its(:account_name) { should =~ /\w\d/ }
|
53
53
|
its(:key) { should be_a String }
|
@@ -59,7 +59,7 @@ describe IB::Messages do
|
|
59
59
|
context "received :AccountDownloadEnd message" do
|
60
60
|
subject { @received[:AccountDownloadEnd].first }
|
61
61
|
|
62
|
-
it {
|
62
|
+
it { should be_an IB::Messages::Incoming::AccountDownloadEnd }
|
63
63
|
its(:data) { should be_a Hash }
|
64
64
|
its(:account_name) { should =~ /\w\d/ }
|
65
65
|
its(:to_human) { should =~ /AccountDownloadEnd/ }
|
@@ -68,7 +68,7 @@ describe IB::Messages do
|
|
68
68
|
context "received :PortfolioValue message" do
|
69
69
|
subject { @received[:PortfolioValue].first }
|
70
70
|
|
71
|
-
it {
|
71
|
+
it { should be_an IB::Messages::Incoming::PortfolioValue }
|
72
72
|
its(:contract) { should be_a IB::Models::Contract }
|
73
73
|
its(:data) { should be_a Hash }
|
74
74
|
its(:position) { should be_a Integer }
|
@@ -6,17 +6,19 @@ describe IB::Messages do
|
|
6
6
|
|
7
7
|
before(:all) do
|
8
8
|
connect_and_receive :NextValidID, :OpenOrderEnd, :Alert
|
9
|
-
wait_for(2) {
|
9
|
+
wait_for(2) { received? :OpenOrderEnd }
|
10
10
|
end
|
11
11
|
|
12
12
|
after(:all) { close_connection }
|
13
13
|
|
14
14
|
it 'receives :NextValidID message' do
|
15
15
|
@received[:NextValidID].should_not be_empty
|
16
|
+
@received[:NextValidID].first.should be_an IB::Messages::Incoming::NextValidID
|
16
17
|
end
|
17
18
|
|
18
19
|
it 'receives :OpenOrderEnd message' do
|
19
20
|
@received[:OpenOrderEnd].should_not be_empty
|
21
|
+
@received[:OpenOrderEnd].first.should be_an IB::Messages::Incoming::OpenOrderEnd
|
20
22
|
end
|
21
23
|
|
22
24
|
it 'logs connection notification' do
|
@@ -23,7 +23,7 @@ describe IB::Messages do
|
|
23
23
|
context "received :Alert message " do
|
24
24
|
subject { @received[:Alert].first }
|
25
25
|
|
26
|
-
it {
|
26
|
+
it { should be_an IB::Messages::Incoming::Alert }
|
27
27
|
it { should be_warning }
|
28
28
|
it { should_not be_error }
|
29
29
|
its(:code) { should be_an Integer }
|
@@ -34,7 +34,7 @@ describe IB::Messages do
|
|
34
34
|
context "received :TickPrice message" do
|
35
35
|
subject { @received[:TickPrice].first }
|
36
36
|
|
37
|
-
it {
|
37
|
+
it { should be_an IB::Messages::Incoming::TickPrice}
|
38
38
|
its(:tick_type) { should be_an Integer }
|
39
39
|
its(:type) { should be_a Symbol }
|
40
40
|
its(:price) { should be_a Float }
|
@@ -47,7 +47,7 @@ describe IB::Messages do
|
|
47
47
|
context "received :TickSize message" do
|
48
48
|
subject { @received[:TickSize].first }
|
49
49
|
|
50
|
-
it {
|
50
|
+
it { should be_an IB::Messages::Incoming::TickSize }
|
51
51
|
its(:type) { should_not be_nil }
|
52
52
|
its(:data) { should be_a Hash }
|
53
53
|
its(:tick_type) { should be_an Integer }
|