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.
Files changed (50) hide show
  1. data/HISTORY +4 -0
  2. data/TODO +0 -3
  3. data/VERSION +1 -1
  4. data/bin/contract_details +3 -3
  5. data/bin/depth_of_market +1 -1
  6. data/bin/historic_data +5 -8
  7. data/bin/market_data +2 -2
  8. data/bin/option_data +2 -2
  9. data/lib/ib-ruby/connection.rb +1 -9
  10. data/lib/ib-ruby/extensions.rb +8 -0
  11. data/lib/ib-ruby/messages/abstract_message.rb +89 -0
  12. data/lib/ib-ruby/messages/incoming.rb +415 -487
  13. data/lib/ib-ruby/messages/outgoing.rb +241 -305
  14. data/lib/ib-ruby/models/bar.rb +3 -3
  15. data/lib/ib-ruby/models/contract/bag.rb +1 -5
  16. data/lib/ib-ruby/models/contract.rb +50 -33
  17. data/lib/ib-ruby/models/execution.rb +6 -3
  18. data/lib/ib-ruby/models/order.rb +7 -5
  19. data/lib/ib-ruby/socket.rb +13 -0
  20. data/lib/ib-ruby/symbols/forex.rb +7 -14
  21. data/lib/ib-ruby/symbols/futures.rb +16 -20
  22. data/lib/ib-ruby/symbols/options.rb +6 -4
  23. data/lib/ib-ruby/symbols/stocks.rb +1 -1
  24. data/lib/ib-ruby.rb +1 -0
  25. data/spec/README.md +34 -0
  26. data/spec/ib-ruby/connection_spec.rb +4 -4
  27. data/spec/ib-ruby/messages/incoming_spec.rb +50 -0
  28. data/spec/ib-ruby/messages/outgoing_spec.rb +32 -0
  29. data/spec/ib-ruby/models/contract_spec.rb +27 -25
  30. data/spec/ib-ruby/models/order_spec.rb +56 -23
  31. data/spec/integration/account_info_spec.rb +85 -0
  32. data/spec/integration/contract_info_spec.rb +209 -0
  33. data/spec/integration/depth_data_spec.rb +46 -0
  34. data/spec/integration/historic_data_spec.rb +82 -0
  35. data/spec/integration/market_data_spec.rb +97 -0
  36. data/spec/integration/option_data_spec.rb +96 -0
  37. data/spec/integration/orders/execution_spec.rb +135 -0
  38. data/spec/{ib-ruby/messages → integration/orders}/open_order +9 -205
  39. data/spec/integration/orders/placement_spec.rb +150 -0
  40. data/spec/integration/orders/valid_ids_spec.rb +84 -0
  41. data/spec/integration_helper.rb +110 -0
  42. data/spec/message_helper.rb +13 -18
  43. data/spec/spec_helper.rb +35 -26
  44. metadata +33 -17
  45. data/spec/ib-ruby/messages/README.md +0 -16
  46. data/spec/ib-ruby/messages/account_info_spec.rb +0 -84
  47. data/spec/ib-ruby/messages/just_connect_spec.rb +0 -33
  48. data/spec/ib-ruby/messages/market_data_spec.rb +0 -92
  49. data/spec/ib-ruby/messages/orders_spec.rb +0 -219
  50. data/spec/ib-ruby_spec.rb +0 -0
@@ -47,40 +47,73 @@ describe IB::Models::Order do
47
47
 
48
48
  it 'allows setting attributes' do
49
49
  x = IB::Models::Order.new
50
- expect {
51
- x.outside_rth = true
52
- x.open_close = 'C'
53
- x.origin = IB::Models::Order::Origin_Firm
54
- x.transmit = false
55
- x.designated_location = "WHATEVER"
56
- x.exempt_code = 123
57
- x.delta_neutral_order_type = "HACK"
58
- x.what_if = true
59
- x.not_held = true
60
- }.to_not raise_error
61
-
62
- x.outside_rth.should == true
63
- x.open_close.should == 'C'
64
- x.origin.should == IB::Models::Order::Origin_Firm
65
- x.transmit.should == false
66
- x.designated_location.should == "WHATEVER"
67
- x.exempt_code.should == 123
68
- x.delta_neutral_order_type.should == "HACK"
69
- x.what_if.should == true
70
- x.not_held.should == true
50
+ properties.each do |name, value|
51
+ subject.send("#{name}=", value)
52
+ subject.send(name).should == value
53
+ end
71
54
  end
72
55
  end #instantiation
73
56
 
74
57
  context 'equality' do
75
58
  subject { IB::Models::Order.new properties }
76
59
 
77
- it 'be self-equal ' do
60
+ it 'is self-equal ' do
78
61
  should == subject
79
62
  end
80
63
 
81
- it 'be equal to Order with the same properties' do
64
+ it 'is equal to Order with the same properties' do
82
65
  should == IB::Models::Order.new(properties)
83
66
  end
67
+
68
+ it 'is not equal for Orders with different limit price' do
69
+ order1 = IB::Models::Order.new :total_quantity => 100,
70
+ :limit_price => 1,
71
+ :action => 'BUY'
72
+
73
+ order2 = IB::Models::Order.new :total_quantity => 100,
74
+ :limit_price => 2,
75
+ :action => 'BUY'
76
+ order1.should_not == order2
77
+ order2.should_not == order1
78
+ end
79
+
80
+ it 'is not equal for Orders with different total_quantity' do
81
+ order1 = IB::Models::Order.new :total_quantity => 20000,
82
+ :limit_price => 1,
83
+ :action => 'BUY'
84
+
85
+ order2 = IB::Models::Order.new :total_quantity => 100,
86
+ :action => 'BUY',
87
+ :limit_price => 1
88
+ order1.should_not == order2
89
+ order2.should_not == order1
90
+ end
91
+
92
+ it 'is not equal for Orders with different action/side' do
93
+ order1 = IB::Models::Order.new :total_quantity => 100,
94
+ :limit_price => 1,
95
+ :action => 'SELL'
96
+
97
+ order2 = IB::Models::Order.new :total_quantity => 100,
98
+ :action => 'BUY',
99
+ :limit_price => 1
100
+ order1.should_not == order2
101
+ order2.should_not == order1
102
+ end
103
+
104
+ it 'is not equal for Orders with different order_type' do
105
+ order1 = IB::Models::Order.new :total_quantity => 100,
106
+ :limit_price => 1,
107
+ :action => 'BUY',
108
+ :order_type => 'LMT'
109
+
110
+ order2 = IB::Models::Order.new :total_quantity => 100,
111
+ :action => 'BUY',
112
+ :limit_price => 1,
113
+ :order_type => 'MKT'
114
+ order1.should_not == order2
115
+ order2.should_not == order1
116
+ end
84
117
  end
85
118
 
86
119
  end # describe IB::Models::Order
@@ -0,0 +1,85 @@
1
+ require 'integration_helper'
2
+
3
+ shared_examples_for 'Valid account data request' do
4
+
5
+ after(:all) do
6
+ @ib.send_message :RequestAccountData, :subscribe => false
7
+ clean_connection
8
+ end
9
+
10
+ context "received :AccountUpdateTime message" do
11
+ subject { @received[:AccountUpdateTime].first }
12
+
13
+ it { should be_an IB::Messages::Incoming::AccountUpdateTime }
14
+ its(:data) { should be_a Hash }
15
+ its(:time_stamp) { should =~ /\d\d:\d\d/ }
16
+ its(:to_human) { should =~ /AccountUpdateTime/ }
17
+ end
18
+
19
+ context "received :AccountValue message" do
20
+ subject { @received[:AccountValue].first }
21
+
22
+ #ps
23
+ it { should be_an IB::Messages::Incoming::AccountValue }
24
+ its(:data) { should be_a Hash }
25
+ its(:account_name) { should =~ /\w\d/ }
26
+ its(:key) { should be_a String }
27
+ its(:value) { should be_a String }
28
+ its(:currency) { should be_a String }
29
+ its(:to_human) { should =~ /AccountValue/ }
30
+ end
31
+
32
+ context "received :PortfolioValue message" do
33
+ subject { @received[:PortfolioValue].first }
34
+
35
+ it { should be_an IB::Messages::Incoming::PortfolioValue }
36
+ its(:contract) { should be_a IB::Models::Contract }
37
+ its(:data) { should be_a Hash }
38
+ its(:position) { should be_a Integer }
39
+ its(:market_price) { should be_a Float }
40
+ its(:market_value) { should be_a Float }
41
+ its(:average_cost) { should be_a Float }
42
+ its(:unrealized_pnl) { should be_a Float }
43
+ its(:realized_pnl) { should be_a Float }
44
+ its(:account_name) { should =~ /\w\d/ }
45
+ its(:to_human) { should =~ /PortfolioValue/ }
46
+ end
47
+
48
+ context "received :AccountDownloadEnd message" do
49
+ subject { @received[:AccountDownloadEnd].first }
50
+
51
+ it { should be_an IB::Messages::Incoming::AccountDownloadEnd }
52
+ its(:data) { should be_a Hash }
53
+ its(:account_name) { should =~ /\w\d/ }
54
+ its(:to_human) { should =~ /AccountDownloadEnd/ }
55
+ end
56
+ end
57
+
58
+ describe "Request Account Data", :connected => true, :integration => true do
59
+
60
+ before(:all) do
61
+ verify_account
62
+ connect_and_receive(:Alert, :AccountValue, :AccountDownloadEnd,
63
+ :PortfolioValue, :AccountUpdateTime)
64
+ end
65
+
66
+ after(:all) { close_connection }
67
+
68
+ context "with subscribe option set" do
69
+ before(:all) do
70
+ @ib.send_message :RequestAccountData, :subscribe => true
71
+ wait_for(5) { received? :AccountDownloadEnd }
72
+ end
73
+
74
+ it_behaves_like 'Valid account data request'
75
+ end
76
+
77
+ context "without subscribe option" do
78
+ before(:all) do
79
+ @ib.send_message :RequestAccountData
80
+ wait_for(5) { received? :AccountDownloadEnd }
81
+ end
82
+
83
+ it_behaves_like 'Valid account data request'
84
+ end
85
+ end # Request Account Data
@@ -0,0 +1,209 @@
1
+ require 'integration_helper'
2
+
3
+ describe "Request Contract Info", :connected => true, :integration => true do
4
+
5
+ before(:all) do
6
+ verify_account
7
+ connect_and_receive :NextValidID, :Alert, :ContractData, :ContractDataEnd
8
+ wait_for { received? :NextValidID }
9
+ end
10
+
11
+ after(:all) { close_connection }
12
+
13
+ context "Request Stock data" do
14
+
15
+ before(:all) do
16
+ @contract = IB::Models::Contract.new :symbol => 'AAPL',
17
+ :sec_type => IB::SECURITY_TYPES[:stock]
18
+ @ib.send_message :RequestContractData,
19
+ :id => 111,
20
+ :contract => @contract
21
+
22
+ wait_for(3) { received? :ContractDataEnd }
23
+ end
24
+
25
+ after(:all) { clean_connection } # Clear logs and message collector
26
+
27
+ it { @received[:ContractData].should have_exactly(2).contract_data }
28
+ it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
29
+
30
+ it 'receives Contract Data for requested contract' do
31
+ msg = @received[:ContractData].first
32
+ msg.request_id.should == 111
33
+ msg.contract.should == @contract
34
+ end
35
+
36
+ it 'receives Contract Data with extended fields' do
37
+ # Returns 2 contracts, one for NASDAQ and one for IBIS - internal crossing?
38
+
39
+ @received[:ContractData].each do |msg|
40
+ contract = msg.contract
41
+ contract.symbol.should == 'AAPL'
42
+
43
+ contract.local_symbol.should =~ /AAPL|APC/
44
+ contract.market_name.should =~ /NMS|USSTARS/
45
+ contract.trading_class.should =~ /NMS|USSTARS/
46
+ contract.long_name.should == 'APPLE INC'
47
+ contract.industry.should == 'Technology'
48
+ contract.category.should == 'Computers'
49
+ contract.subcategory.should == 'Computers'
50
+ contract.exchange.should == 'SMART'
51
+ contract.con_id.should be_an Integer
52
+ contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
53
+ contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
54
+ contract.valid_exchanges.should =~ /ISLAND|IBIS/
55
+ contract.order_types.should be_a String
56
+ contract.price_magnifier.should == 1
57
+ contract.min_tick.should be <= 0.01
58
+
59
+ contract.expiry.should be_nil
60
+ end
61
+ end
62
+ end # Stock
63
+
64
+ context "Request Option contract data" do
65
+
66
+ before(:all) do
67
+ @contract = IB::Models::Contract::Option.new :symbol => "AAPL",
68
+ :expiry => "201301",
69
+ :right => "CALL",
70
+ :strike => 500
71
+ @ib.send_message :RequestContractData,
72
+ :id => 123,
73
+ :contract => @contract
74
+
75
+ wait_for(3) { received? :ContractDataEnd }
76
+ end
77
+
78
+ after(:all) { clean_connection } # Clear logs and message collector
79
+
80
+ subject { @received[:ContractData].first }
81
+
82
+ it { @received[:ContractData].should have_exactly(1).contract_data }
83
+ it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
84
+
85
+ it 'receives Contract Data for requested contract' do
86
+ subject.request_id.should == 123
87
+ subject.contract.should == @contract
88
+ end
89
+
90
+ it 'receives Contract Data with extended fields' do
91
+ contract = subject.contract
92
+ contract.symbol.should == 'AAPL'
93
+
94
+ contract.local_symbol.should == 'AAPL 130119C00500000'
95
+ contract.market_name.should == 'AAPL'
96
+ contract.trading_class.should == 'AAPL'
97
+ contract.long_name.should == 'APPLE INC'
98
+ contract.industry.should == 'Technology'
99
+ contract.category.should == 'Computers'
100
+ contract.subcategory.should == 'Computers'
101
+ contract.expiry.should == '20130118'
102
+ contract.exchange.should == 'SMART'
103
+ contract.con_id.should be_an Integer
104
+ contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
105
+ contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
106
+ contract.valid_exchanges.should =~ /CBOE/
107
+ contract.order_types.should be_a String
108
+ contract.price_magnifier.should == 1
109
+ contract.min_tick.should == 0.01
110
+ end
111
+ end # Request Option data
112
+
113
+ context "Request Forex contract data" do
114
+
115
+ before(:all) do
116
+ @contract = IB::Models::Contract.new :symbol => 'EUR', # EURUSD pair
117
+ :currency => "USD",
118
+ :exchange => "IDEALPRO",
119
+ :sec_type => IB::SECURITY_TYPES[:forex]
120
+
121
+ @ib.send_message :RequestContractData,
122
+ :id => 135,
123
+ :contract => @contract
124
+
125
+ wait_for(3) { received? :ContractDataEnd }
126
+ end
127
+
128
+ after(:all) { clean_connection } # Clear logs and message collector
129
+
130
+ subject { @received[:ContractData].first }
131
+
132
+ it { @received[:ContractData].should have_exactly(1).contract_data }
133
+ it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
134
+
135
+ it 'receives Contract Data for requested contract' do
136
+ subject.request_id.should == 135
137
+ subject.contract.should == @contract
138
+ end
139
+
140
+ it 'receives Contract Data with extended fields' do
141
+ contract = subject.contract
142
+ contract.symbol.should == 'EUR'
143
+
144
+ contract.local_symbol.should == 'EUR.USD'
145
+ contract.market_name.should == 'EUR.USD'
146
+ contract.trading_class.should == 'EUR.USD'
147
+ contract.long_name.should == 'European Monetary Union euro'
148
+ contract.industry.should == ''
149
+ contract.category.should == ''
150
+ contract.subcategory.should == ''
151
+ contract.expiry.should be_nil
152
+ contract.exchange.should == 'IDEALPRO'
153
+ contract.con_id.should be_an Integer
154
+ contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
155
+ contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
156
+ contract.valid_exchanges.should =~ /IDEALPRO/
157
+ contract.order_types.should be_a String
158
+ contract.price_magnifier.should == 1
159
+ contract.min_tick.should be <= 0.0001
160
+ end
161
+ end # Request Forex data
162
+
163
+ context "Request Futures contract data" do
164
+
165
+ before(:all) do
166
+ @contract = IB::Symbols::Futures[:ym] # Mini Dow Jones Industrial
167
+
168
+ @ib.send_message :RequestContractData,
169
+ :id => 147,
170
+ :contract => @contract
171
+
172
+ wait_for(3) { received? :ContractDataEnd }
173
+ end
174
+
175
+ after(:all) { clean_connection } # Clear logs and message collector
176
+
177
+ subject { @received[:ContractData].first }
178
+
179
+ it { @received[:ContractData].should have_exactly(1).contract_data }
180
+ it { @received[:ContractDataEnd].should have_exactly(1).contract_data_end }
181
+
182
+ it 'receives Contract Data for requested contract' do
183
+ subject.request_id.should == 147
184
+ subject.contract.should == @contract
185
+ end
186
+
187
+ it 'receives Contract Data with extended fields' do
188
+ contract = subject.contract
189
+ contract.symbol.should == 'YM'
190
+
191
+ contract.local_symbol.should =~ /YM/
192
+ contract.market_name.should == 'YM'
193
+ contract.trading_class.should == 'YM'
194
+ contract.long_name.should == 'Mini Sized Dow Jones Industrial Average $5'
195
+ contract.industry.should == ''
196
+ contract.category.should == ''
197
+ contract.subcategory.should == ''
198
+ contract.expiry.should =~ Regexp.new(IB::Symbols.next_expiry)
199
+ contract.exchange.should == 'ECBOT'
200
+ contract.con_id.should be_an Integer
201
+ contract.trading_hours.should =~ /\d{8}:\d{4}-\d{4}/
202
+ contract.liquid_hours.should =~ /\d{8}:\d{4}-\d{4}/
203
+ contract.valid_exchanges.should =~ /ECBOT/
204
+ contract.order_types.should be_a String
205
+ contract.price_magnifier.should == 1
206
+ contract.min_tick.should == 1
207
+ end
208
+ end # Request Forex data
209
+ end # Contract Data
@@ -0,0 +1,46 @@
1
+ require 'integration_helper'
2
+
3
+ describe 'Request Depth of Market Data', :connected => true,
4
+ :integration => true, :if => :forex_trading_hours do
5
+
6
+ before(:all) do
7
+ verify_account
8
+ connect_and_receive :Alert, :MarketDepth
9
+
10
+ @ib.send_message :RequestMarketDepth, :id => 456, :num_rows => 3,
11
+ :contract => IB::Symbols::Forex[:eurusd]
12
+
13
+ wait_for(3) { received? :MarketDepth, 8 }
14
+ end
15
+
16
+ after(:all) do
17
+ @ib.send_message :CancelMarketDepth, :id => 456
18
+ close_connection
19
+ end
20
+
21
+ subject { @received[:MarketDepth].last }
22
+
23
+ it { @received[:MarketDepth].should have_at_least(8).depth_data }
24
+
25
+ it { should be_an IB::Messages::Incoming::MarketDepth }
26
+ its(:request_id) { should == 456 }
27
+ its(:price) { should be_a Float }
28
+ its(:size) { should be_an Integer }
29
+ its(:to_human) { should =~ /MarketDepth/ }
30
+
31
+ it 'has position field reflecting the row Id of this market depth entry' do
32
+ subject.position.should be_an Integer
33
+ subject.position.should be >= 1
34
+ subject.position.should be <= 3
35
+ end
36
+
37
+ it 'has operation field reflecting how this entry is applied' do
38
+ subject.operation.should be_a Symbol
39
+ subject.operation.to_s.should =~ /insert|update|delete/
40
+ end
41
+
42
+ it 'has side field reflecting side of the book: 0 = ask, 1 = bid' do
43
+ subject.side.should be_a Symbol
44
+ subject.side.to_s.should =~ /ask|bid/
45
+ end
46
+ end # Request Market Depth
@@ -0,0 +1,82 @@
1
+ require 'integration_helper'
2
+
3
+ describe 'Request Historic Data', :connected => true, :integration => true do
4
+
5
+ before(:all) do
6
+ verify_account
7
+ connect_and_receive :Alert, :HistoricalData
8
+ end
9
+
10
+ after(:all) do
11
+ @ib.send_message :CancelHistoricalData, :id => 456
12
+ close_connection
13
+ end
14
+
15
+ context 'Wrong Requests' do
16
+ it 'raises if incorrect bar size' do
17
+ expect do
18
+ @ib.send_message :RequestHistoricalData, :id => 456,
19
+ :contract => IB::Symbols::Stocks[:wfc],
20
+ :end_date_time => Time.now.to_ib,
21
+ :duration => '1 D',
22
+ :bar_size => '11 min',
23
+ :what_to_show => :trades,
24
+ :format_date => 1
25
+ end.to raise_error /bar_size must be one of/
26
+ end
27
+
28
+ it 'raises if incorrect what_to_show' do
29
+ expect do
30
+ @ib.send_message :RequestHistoricalData, :id => 456,
31
+ :contract => IB::Symbols::Stocks[:wfc],
32
+ :end_date_time => Time.now.to_ib,
33
+ :duration => '1 D',
34
+ :bar_size => '15 mins',
35
+ :what_to_show => :nonsense,
36
+ :format_date => 1
37
+ end.to raise_error /:what_to_show must be one of/
38
+ end
39
+ end
40
+
41
+ context 'Correct Request' do
42
+ before(:all) do
43
+ # No historical data for GBP/CASH@IDEALPRO
44
+ @ib.send_message :RequestHistoricalData, :id => 456,
45
+ :contract => IB::Symbols::Stocks[:wfc],
46
+ :end_date_time => Time.now.to_ib,
47
+ :duration => '1 D',
48
+ :bar_size => '15 mins',
49
+ :what_to_show => :trades,
50
+ :format_date => 1
51
+
52
+ wait_for(3) { received? :HistoricalData }
53
+ end
54
+
55
+ subject { @received[:HistoricalData].last }
56
+
57
+ it { @received[:HistoricalData].should have_at_least(1).historic_data }
58
+
59
+ it { should be_an IB::Messages::Incoming::HistoricalData }
60
+ its(:request_id) { should == 456 }
61
+ its(:count) { should be_an Integer }
62
+ its(:start_date) { should =~ /\d{8} *\d\d:\d\d:\d\d/ } # "20120302 22:46:42"
63
+ its(:end_date) { should =~ /\d{8} *\d\d:\d\d:\d\d/ }
64
+ its(:to_human) { should =~ /HistoricalData/ }
65
+
66
+ it 'has results Array with returned historic data' do
67
+ subject.results.should be_an Array
68
+ subject.results.size.should == subject.count
69
+ subject.results.each do |bar|
70
+ bar.should be_an IB::Models::Bar
71
+ bar.time.should =~ /\d{8} *\d\d:\d\d:\d\d/
72
+ bar.open.should be_a Float
73
+ bar.high.should be_a Float
74
+ bar.low.should be_a Float
75
+ bar.close.should be_a Float
76
+ bar.wap.should be_a Float
77
+ bar.trades.should be_an Integer
78
+ bar.volume.should be_an Integer
79
+ end
80
+ end
81
+ end
82
+ end # Request Historic Data
@@ -0,0 +1,97 @@
1
+ require 'integration_helper'
2
+
3
+ describe 'Request Market Data', :connected => true, :integration => true do
4
+
5
+ context 'when subscribed to :Tick... messages' do
6
+
7
+ before(:all) do
8
+ verify_account
9
+ connect_and_receive :Alert, :TickPrice, :TickSize
10
+
11
+ ##TODO consider a follow the sun market lookup for windening the types tested
12
+ @ib.send_message :RequestMarketData, :id => 456,
13
+ :contract => IB::Symbols::Forex[:eurusd]
14
+ wait_for(3) { received? :TickPrice }
15
+ end
16
+
17
+ after(:all) do
18
+ @ib.send_message :CancelMarketData, :id => 456
19
+ close_connection
20
+ end
21
+
22
+ context "received :Alert message " do
23
+ subject { @received[:Alert].first }
24
+
25
+ it { should be_an IB::Messages::Incoming::Alert }
26
+ it { should be_warning }
27
+ it { should_not be_error }
28
+ its(:code) { should be_an Integer }
29
+ its(:message) { should =~ /Market data farm connection is OK/ }
30
+ its(:to_human) { should =~ /TWS Warning/ }
31
+ end
32
+
33
+ context "received :TickPrice message" do
34
+ subject { @received[:TickPrice].first }
35
+
36
+ it { should be_an IB::Messages::Incoming::TickPrice }
37
+ its(:tick_type) { should be_an Integer }
38
+ its(:type) { should be_a Symbol }
39
+ its(:price) { should be_a Float }
40
+ its(:size) { should be_an Integer }
41
+ its(:data) { should be_a Hash }
42
+ its(:ticker_id) { should == 456 } # ticker_id
43
+ its(:to_human) { should =~ /TickPrice/ }
44
+ end
45
+
46
+ context "received :TickSize message", :if => :forex_trading_hours do
47
+ before(:all) do
48
+ wait_for(3) { received? :TickSize }
49
+ end
50
+
51
+ subject { @received[:TickSize].first }
52
+
53
+ it { should be_an IB::Messages::Incoming::TickSize }
54
+ its(:type) { should_not be_nil }
55
+ its(:data) { should be_a Hash }
56
+ its(:tick_type) { should be_an Integer }
57
+ its(:type) { should be_a Symbol }
58
+ its(:size) { should be_an Integer }
59
+ its(:ticker_id) { should == 456 }
60
+ its(:to_human) { should =~ /TickSize/ }
61
+ end
62
+ end # when subscribed to :Tick... messages
63
+
64
+ context 'when NOT subscribed to :Tick... messages', :slow => true do
65
+
66
+ before(:all) do
67
+ connect_and_receive :NextValidID
68
+
69
+ @ib.send_message :RequestMarketData, :id => 456,
70
+ :contract => IB::Symbols::Forex[:eurusd]
71
+ wait_for(2)
72
+ end
73
+
74
+ after(:all) do
75
+ @ib.send_message :CancelMarketData, :id => 456
76
+ close_connection
77
+ end
78
+
79
+ it "logs warning about unhandled :OpenOrderEnd message" do
80
+ should_log /No subscribers for message .*:OpenOrderEnd/
81
+ end
82
+
83
+ it "logs warning about unhandled :Alert message" do
84
+ should_log /No subscribers for message .*:Alert/
85
+ end
86
+
87
+ it "logs warning about unhandled :Tick... messages" do
88
+ should_log /No subscribers for message .*:TickPrice/
89
+ end
90
+
91
+ it "logs warning about unhandled :Tick... messages", :if => :forex_trading_hours do
92
+ should_log /No subscribers for message .*:TickSize/
93
+ end
94
+
95
+ end # NOT subscribed to :Tick... messages
96
+
97
+ end # Request Market Data