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.
- 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
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
def wait_for_all_ticks
|
4
|
+
wait_for(5) do
|
5
|
+
received?(:TickPrice) && received?(:TickSize) &&
|
6
|
+
received?(:TickOption) && received?(:TickString)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'Request Market Data for Options', :if => :us_trading_hours,
|
11
|
+
:connected => true, :integration => true do
|
12
|
+
|
13
|
+
before(:all) do
|
14
|
+
verify_account
|
15
|
+
connect_and_receive :Alert, :TickPrice, :TickSize, :TickOption, :TickString
|
16
|
+
|
17
|
+
@ib.send_message :RequestMarketData, :id => 456,
|
18
|
+
:contract => IB::Symbols::Options[:aapl500]
|
19
|
+
wait_for_all_ticks
|
20
|
+
end
|
21
|
+
|
22
|
+
after(:all) do
|
23
|
+
@ib.send_message :CancelMarketData, :id => 456
|
24
|
+
close_connection
|
25
|
+
end
|
26
|
+
|
27
|
+
context "received :Alert message " do
|
28
|
+
subject { @received[:Alert].first }
|
29
|
+
|
30
|
+
it { should be_an IB::Messages::Incoming::Alert }
|
31
|
+
it { should be_warning }
|
32
|
+
it { should_not be_error }
|
33
|
+
its(:code) { should be_an Integer }
|
34
|
+
its(:message) { should =~ /Market data farm connection is OK/ }
|
35
|
+
its(:to_human) { should =~ /TWS Warning/ }
|
36
|
+
end
|
37
|
+
|
38
|
+
context "received :TickPrice message" do
|
39
|
+
subject { @received[:TickPrice].first }
|
40
|
+
|
41
|
+
it { should be_an IB::Messages::Incoming::TickPrice }
|
42
|
+
its(:tick_type) { should be_an Integer }
|
43
|
+
its(:type) { should be_a Symbol }
|
44
|
+
its(:price) { should be_a Float }
|
45
|
+
its(:size) { should be_an Integer }
|
46
|
+
its(:can_auto_execute) { should be_an Integer }
|
47
|
+
its(:data) { should be_a Hash }
|
48
|
+
its(:ticker_id) { should == 456 } # ticker_id
|
49
|
+
its(:to_human) { should =~ /TickPrice/ }
|
50
|
+
end
|
51
|
+
|
52
|
+
context "received :TickSize message" do
|
53
|
+
subject { @received[:TickSize].first }
|
54
|
+
|
55
|
+
it { should be_an IB::Messages::Incoming::TickSize }
|
56
|
+
its(:type) { should_not be_nil }
|
57
|
+
its(:data) { should be_a Hash }
|
58
|
+
its(:tick_type) { should be_an Integer }
|
59
|
+
its(:type) { should be_a Symbol }
|
60
|
+
its(:size) { should be_an Integer }
|
61
|
+
its(:ticker_id) { should == 456 }
|
62
|
+
its(:to_human) { should =~ /TickSize/ }
|
63
|
+
end
|
64
|
+
|
65
|
+
context "received :TickOption message" do
|
66
|
+
subject { @received[:TickOption].first }
|
67
|
+
|
68
|
+
it { should be_an IB::Messages::Incoming::TickOption }
|
69
|
+
its(:type) { should_not be_nil }
|
70
|
+
its(:data) { should be_a Hash }
|
71
|
+
its(:tick_type) { should be_an Integer }
|
72
|
+
its(:type) { should be_a Symbol }
|
73
|
+
its(:under_price) { should be_a Float }
|
74
|
+
its(:option_price) { should be_a Float }
|
75
|
+
its(:pv_dividend) { should be_a Float }
|
76
|
+
its(:implied_volatility) { should be_a Float }
|
77
|
+
its(:gamma) { should be_a Float }
|
78
|
+
its(:vega) { should be_a Float }
|
79
|
+
its(:theta) { should be_a Float }
|
80
|
+
its(:ticker_id) { should == 456 }
|
81
|
+
its(:to_human) { should =~ /TickOption/ }
|
82
|
+
end
|
83
|
+
|
84
|
+
context "received :TickString message" do
|
85
|
+
subject { @received[:TickString].first }
|
86
|
+
|
87
|
+
it { should be_an IB::Messages::Incoming::TickString }
|
88
|
+
its(:type) { should_not be_nil }
|
89
|
+
its(:data) { should be_a Hash }
|
90
|
+
its(:tick_type) { should be_an Integer }
|
91
|
+
its(:type) { should be_a Symbol }
|
92
|
+
its(:value) { should be_a String }
|
93
|
+
its(:ticker_id) { should == 456 }
|
94
|
+
its(:to_human) { should =~ /TickString/ }
|
95
|
+
end
|
96
|
+
end # Request Options Market Data
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
# TODO: RequestExecutions (with filters?)
|
4
|
+
|
5
|
+
def wait_for_execution_and_commission
|
6
|
+
wait_for(5) do
|
7
|
+
received?(:ExecutionData) && received?(:OpenOrder) &&
|
8
|
+
@received[:OpenOrder].last.order.commission
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Trades", :connected => true, :integration => true, :slow => true do
|
13
|
+
|
14
|
+
before(:all) { verify_account }
|
15
|
+
|
16
|
+
context "Trading Forex", :if => :forex_trading_hours do
|
17
|
+
|
18
|
+
before(:all) do
|
19
|
+
@contract = IB::Symbols::Forex[:eurusd]
|
20
|
+
connect_and_receive :NextValidID, :Alert, :ExecutionData, :ExecutionDataEnd,
|
21
|
+
:OpenOrder, :OrderStatus, :OpenOrderEnd
|
22
|
+
wait_for { received? :NextValidID }
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:all) { close_connection }
|
26
|
+
|
27
|
+
context "Placing BUY order" do
|
28
|
+
|
29
|
+
before(:all) do
|
30
|
+
place_order @contract,
|
31
|
+
:total_quantity => 20000,
|
32
|
+
:limit_price => 2,
|
33
|
+
:action => 'BUY'
|
34
|
+
|
35
|
+
wait_for_execution_and_commission
|
36
|
+
end
|
37
|
+
|
38
|
+
after(:all) do
|
39
|
+
clean_connection # Clear logs and message collector
|
40
|
+
@ib.cancel_order @order_id_placed # Just in case...
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'changes client`s next_order_id' do
|
44
|
+
@order_id_placed = @order_id_before
|
45
|
+
@ib.next_order_id.should == @order_id_before + 1
|
46
|
+
end
|
47
|
+
|
48
|
+
it { @received[:OpenOrder].should have_at_least(1).open_order_message }
|
49
|
+
it { @received[:OrderStatus].should have_at_least(1).status_message }
|
50
|
+
it { @received[:ExecutionData].should have_exactly(1).execution_data }
|
51
|
+
it { @received[:ExecutionDataEnd].should be_empty }
|
52
|
+
|
53
|
+
it 'receives filled OpenOrder' do
|
54
|
+
open_order_should_be 'Filled', -1
|
55
|
+
msg = @received[:OpenOrder].last
|
56
|
+
msg.order.commission.should == 2.5
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'receives Execution Data' do
|
60
|
+
execution_should_be 'BUY'
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'receives OrderStatus with fill details' do
|
64
|
+
order_status_should_be 'Filled', -1
|
65
|
+
end
|
66
|
+
end # Placing BUY
|
67
|
+
|
68
|
+
context "Placing SELL order" do
|
69
|
+
|
70
|
+
before(:all) do
|
71
|
+
place_order @contract,
|
72
|
+
:total_quantity => 20000,
|
73
|
+
:limit_price => 1,
|
74
|
+
:action => 'SELL'
|
75
|
+
|
76
|
+
wait_for_execution_and_commission
|
77
|
+
end
|
78
|
+
|
79
|
+
after(:all) do
|
80
|
+
clean_connection # Clear logs and message collector
|
81
|
+
@ib.cancel_order @order_id_placed # Just in case...
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'changes client`s next_order_id' do
|
85
|
+
@order_id_placed = @order_id_before
|
86
|
+
@ib.next_order_id.should == @order_id_before + 1
|
87
|
+
end
|
88
|
+
|
89
|
+
it { @received[:OpenOrder].should have_at_least(1).open_order_message }
|
90
|
+
it { @received[:OrderStatus].should have_at_least(1).status_message }
|
91
|
+
it { @received[:ExecutionData].should have_exactly(1).execution_data }
|
92
|
+
|
93
|
+
it 'receives filled OpenOrder' do
|
94
|
+
open_order_should_be 'Filled', -1
|
95
|
+
msg = @received[:OpenOrder].last
|
96
|
+
msg.order.commission.should == 2.5
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'receives Execution Data' do
|
100
|
+
execution_should_be 'SELL'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'receives OrderStatus with fill details' do
|
104
|
+
order_status_should_be 'Filled', -1
|
105
|
+
end
|
106
|
+
end # Placing SELL
|
107
|
+
|
108
|
+
context "Request executions" do
|
109
|
+
|
110
|
+
before(:all) do
|
111
|
+
@ib.send_message :RequestExecutions,
|
112
|
+
:request_id => 456,
|
113
|
+
:client_id => OPTS[:connection][:client_id],
|
114
|
+
:time => (Time.now-10).to_ib
|
115
|
+
wait_for(3) { received?(:ExecutionData) }
|
116
|
+
end
|
117
|
+
|
118
|
+
#after(:all) { clean_connection }
|
119
|
+
|
120
|
+
it 'does not receive Order-related messages' do
|
121
|
+
@received[:OpenOrder].should be_empty
|
122
|
+
@received[:OrderStatus].should be_empty
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'receives ExecutionData messages' do
|
126
|
+
@received[:ExecutionData].should have_at_least(1).execution_data
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'receives Execution Data' do
|
130
|
+
execution_should_be 'SELL', :request_id => 456
|
131
|
+
end
|
132
|
+
end # Request executions
|
133
|
+
end # Forex order
|
134
|
+
|
135
|
+
end # Trades
|
@@ -19,211 +19,15 @@
|
|
19
19
|
@socket=nil>, @socket=nil>, @socket=nil>]
|
20
20
|
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
22
|
+
#<IB::Models::Order:0xfa0 #<IB::Models::Order:0x1040 #<IB::Models::Order:0x10e0
|
23
|
+
@commission=nil, @commission=nil, @commission=2.5,
|
24
|
+
@commission_currency="USD", @commission_currency="USD", @commission_currency="USD",
|
25
|
+
@limit_price=1.0, @limit_price=1.0, @limit_price=1.0,
|
26
|
+
@status="Filled", @status="Filled", @status="Filled",
|
27
|
+
@tif="DAY", @tif="DAY", @tif="DAY",
|
28
|
+
@total_quantity=20000, @total_quantity=20000, @total_quantity=20000,
|
29
|
+
@transmit=true, @transmit=true, @transmit=true,
|
30
|
+
@what_if=false> @what_if=false> @what_if=false>
|
227
31
|
|
228
32
|
[#<IB::Messages::Incoming::OrderStatus:0x127c
|
229
33
|
@created_at=Thu Feb 23 12:17:48 +0400 2012,
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe "Orders", :connected => true, :integration => true do
|
4
|
+
|
5
|
+
before(:all) { verify_account }
|
6
|
+
|
7
|
+
context "Placing wrong order", :slow => true do
|
8
|
+
|
9
|
+
before(:all) do
|
10
|
+
connect_and_receive :NextValidID, :Alert, :OpenOrder, :OrderStatus
|
11
|
+
wait_for { received? :NextValidID }
|
12
|
+
|
13
|
+
place_order IB::Symbols::Stocks[:wfc],
|
14
|
+
:limit_price => 9.131313 # Weird non-acceptable price
|
15
|
+
wait_for 1
|
16
|
+
end
|
17
|
+
|
18
|
+
after(:all) { close_connection }
|
19
|
+
|
20
|
+
it 'does not place new Order' do
|
21
|
+
@received[:OpenOrder].should be_empty
|
22
|
+
@received[:OrderStatus].should be_empty
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'still changes client`s next_order_id' do
|
26
|
+
@order_id_placed.should == @order_id_before
|
27
|
+
@ib.next_order_id.should == @order_id_before + 1
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'received :Alert message' do
|
31
|
+
subject { @received[:Alert].last }
|
32
|
+
|
33
|
+
it { should be_an IB::Messages::Incoming::Alert }
|
34
|
+
it { should be_error }
|
35
|
+
its(:code) { should be_a Integer }
|
36
|
+
its(:message) { should =~ /The price does not conform to the minimum price variation for this contract/ }
|
37
|
+
end
|
38
|
+
|
39
|
+
end # Placing wrong order
|
40
|
+
|
41
|
+
context "Off-market stock order" do
|
42
|
+
before(:all) do
|
43
|
+
connect_and_receive :NextValidID, :Alert, :OpenOrder, :OrderStatus, :OpenOrderEnd
|
44
|
+
wait_for { received? :NextValidID }
|
45
|
+
|
46
|
+
place_order IB::Symbols::Stocks[:wfc],
|
47
|
+
:limit_price => 9.13 # Set acceptable price
|
48
|
+
wait_for { @received[:OpenOrder].size > 2 && @received[:OpenOrder].size > 1 }
|
49
|
+
end
|
50
|
+
|
51
|
+
after(:all) { close_connection }
|
52
|
+
|
53
|
+
context "Placing" do
|
54
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
55
|
+
|
56
|
+
it 'changes client`s next_order_id' do
|
57
|
+
@order_id_placed.should == @order_id_before
|
58
|
+
@ib.next_order_id.should == @order_id_before + 1
|
59
|
+
end
|
60
|
+
|
61
|
+
it { @received[:OpenOrder].should have_at_least(1).open_order_message }
|
62
|
+
it { @received[:OrderStatus].should have_at_least(1).status_message }
|
63
|
+
|
64
|
+
it 'receives confirmation of Order submission' do
|
65
|
+
open_order_should_be /Submitted/ # ()Pre)Submitted
|
66
|
+
order_status_should_be /Submitted/
|
67
|
+
end
|
68
|
+
end # Placing
|
69
|
+
|
70
|
+
context "Retrieving placed orders" do
|
71
|
+
before(:all) do
|
72
|
+
@ib.send_message :RequestAllOpenOrders
|
73
|
+
|
74
|
+
wait_for { received?(:OpenOrderEnd) }
|
75
|
+
end
|
76
|
+
|
77
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
78
|
+
|
79
|
+
it 'does not increase client`s next_order_id further' do
|
80
|
+
@ib.next_order_id.should == @order_id_after
|
81
|
+
end
|
82
|
+
|
83
|
+
it { @received[:OpenOrder].should have_exactly(1).open_order_message }
|
84
|
+
it { @received[:OrderStatus].should have_exactly(1).status_message }
|
85
|
+
it { @received[:OpenOrderEnd].should have_exactly(1).order_end_message }
|
86
|
+
it { @received[:Alert].should have_exactly(0).alert_messages }
|
87
|
+
|
88
|
+
it 'receives OpenOrder and OrderStatus for placed order' do
|
89
|
+
open_order_should_be /Submitted/
|
90
|
+
order_status_should_be /Submitted/
|
91
|
+
end
|
92
|
+
end # Retrieving
|
93
|
+
|
94
|
+
context "Cancelling placed order" do
|
95
|
+
before(:all) do
|
96
|
+
@ib.cancel_order @order_id_placed
|
97
|
+
|
98
|
+
wait_for { received?(:OrderStatus) && received?(:Alert) }
|
99
|
+
end
|
100
|
+
|
101
|
+
after(:all) { clean_connection } # Clear logs and message collector
|
102
|
+
|
103
|
+
it 'does not increase client`s next_order_id further' do
|
104
|
+
@ib.next_order_id.should == @order_id_after
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'does not receive OpenOrder message' do
|
108
|
+
received?(:OpenOrder).should be_false
|
109
|
+
end
|
110
|
+
|
111
|
+
it { @received[:OrderStatus].should have_exactly(1).status_message }
|
112
|
+
it { @received[:Alert].should have_exactly(1).alert_message }
|
113
|
+
|
114
|
+
it 'receives cancellation Order Status' do
|
115
|
+
order_status_should_be /Cancel/ # Cancelled / PendingCancel
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'receives Order cancelled Alert' do
|
119
|
+
alert = @received[:Alert].first
|
120
|
+
alert.should be_an IB::Messages::Incoming::Alert
|
121
|
+
alert.message.should =~ /Order Canceled - reason:/
|
122
|
+
end
|
123
|
+
end # Cancelling
|
124
|
+
|
125
|
+
context "Cancelling wrong order" do
|
126
|
+
before(:all) do
|
127
|
+
@ib.cancel_order rand(99999999)
|
128
|
+
|
129
|
+
wait_for { received?(:Alert) }
|
130
|
+
end
|
131
|
+
|
132
|
+
it { @received[:Alert].should have_exactly(1).alert_message }
|
133
|
+
|
134
|
+
it 'does not increase client`s next_order_id further' do
|
135
|
+
@ib.next_order_id.should == @order_id_after
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'does not receive Order messages' do
|
139
|
+
received?(:OrderStatus).should be_false
|
140
|
+
received?(:OpenOrder).should be_false
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'receives unable to find Order Alert' do
|
144
|
+
alert = @received[:Alert].first
|
145
|
+
alert.should be_an IB::Messages::Incoming::Alert
|
146
|
+
alert.message.should =~ /Can't find order with id =/
|
147
|
+
end
|
148
|
+
end # Cancelling
|
149
|
+
end # Off-market order
|
150
|
+
end # Orders
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
shared_examples_for 'Received single id' do
|
4
|
+
subject { @received[:NextValidID].first }
|
5
|
+
|
6
|
+
after(:all) { clean_connection }
|
7
|
+
|
8
|
+
it { @received[:NextValidID].should have_exactly(1).message }
|
9
|
+
|
10
|
+
it 'receives next valid for Order placement' do
|
11
|
+
subject.should be_an IB::Messages::Incoming::NextValidID
|
12
|
+
subject.order_id.should be_an Integer
|
13
|
+
@id[:at_connect] ||= subject.order_id # just assign once
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'logs next valid order id' do
|
17
|
+
should_log /Got next valid order id/
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
shared_examples_for 'Received single id after request' do
|
22
|
+
subject { @received[:NextValidID].first }
|
23
|
+
|
24
|
+
it_behaves_like 'Received single id'
|
25
|
+
|
26
|
+
it 'no new id is generated by this request' do
|
27
|
+
subject.order_id.should == @id[:at_connect]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'does not receive :OpenOrderEnd message' do
|
31
|
+
@received[:OpenOrderEnd].should be_empty
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not reconnect to server' do
|
35
|
+
should_not_log /Connected to server/
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'Ids valid for Order placement', :connected => true, :integration => true do
|
40
|
+
|
41
|
+
before(:all) do
|
42
|
+
verify_account
|
43
|
+
connect_and_receive :NextValidID, :OpenOrderEnd, :Alert
|
44
|
+
wait_for(2) { received? :OpenOrderEnd }
|
45
|
+
@id = {} # Moving id between contexts. Feels dirty.
|
46
|
+
end
|
47
|
+
|
48
|
+
after(:all) { close_connection }
|
49
|
+
|
50
|
+
context 'at connect' do
|
51
|
+
|
52
|
+
it_behaves_like 'Received single id'
|
53
|
+
|
54
|
+
it { @received[:OpenOrderEnd].should have_exactly(1).message }
|
55
|
+
|
56
|
+
it 'receives also :OpenOrderEnd message' do
|
57
|
+
@received[:OpenOrderEnd].first.should be_an IB::Messages::Incoming::OpenOrderEnd
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'logs connection notification' do
|
61
|
+
should_log /Connected to server, version: 53, connection time/
|
62
|
+
end
|
63
|
+
end # at connect
|
64
|
+
|
65
|
+
context 'Requesting valid order id' do
|
66
|
+
before(:all) do
|
67
|
+
@ib.send_message :RequestIds
|
68
|
+
wait_for 1 # sec
|
69
|
+
end
|
70
|
+
|
71
|
+
it_behaves_like 'Received single id after request'
|
72
|
+
end # Requesting valid order ids
|
73
|
+
|
74
|
+
context 'Requested number of valid ids is just silently ignored by TWS' do
|
75
|
+
before(:all) do
|
76
|
+
@ib.send_message :RequestIds, :number => 5
|
77
|
+
wait_for 1 # sec
|
78
|
+
end
|
79
|
+
|
80
|
+
it_behaves_like 'Received single id after request'
|
81
|
+
end # number of ids is silently ignored
|
82
|
+
|
83
|
+
end # Ids valid for Order placement
|
84
|
+
|