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