ib-ruby 0.5.19 → 0.5.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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